• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / external-dns / 13883568234

16 Mar 2025 12:54PM UTC coverage: 70.239% (-0.8%) from 71.038%
13883568234

Pull #5186

github

ivankatliarchuk
chore(source): code cleanup to reduce coupling

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
Pull Request #5186: WIP chore(source): code cleanup, decouple and isolate logic

39 of 237 new or added lines in 15 files covered. (16.46%)

1 existing line in 1 file now uncovered.

14130 of 20117 relevant lines covered (70.24%)

670.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/source/utils/utils.go
1
/*
2
Copyright 2025 The Kubernetes Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package utils
18

19
import (
20
        "fmt"
21
        "net/netip"
22
        "strconv"
23
        "strings"
24
        "time"
25

26
        log "github.com/sirupsen/logrus"
27
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
        "k8s.io/apimachinery/pkg/labels"
29

30
        "sigs.k8s.io/external-dns/endpoint"
31
)
32

33
// TTLFromAnnotations TODO: copied from source.go. Refactor to avoid duplication.
34
// TTLFromAnnotations extracts the TTL from the annotations of the given resource.
NEW
35
func TTLFromAnnotations(annotations map[string]string, resource string) endpoint.TTL {
×
NEW
36
        ttlNotConfigured := endpoint.TTL(0)
×
NEW
37
        ttlAnnotation, exists := annotations[ttlAnnotationKey]
×
NEW
38
        if !exists {
×
NEW
39
                return ttlNotConfigured
×
NEW
40
        }
×
NEW
41
        ttlValue, err := parseTTL(ttlAnnotation)
×
NEW
42
        if err != nil {
×
NEW
43
                log.Warnf("%s: \"%v\" is not a valid TTL value: %v", resource, ttlAnnotation, err)
×
NEW
44
                return ttlNotConfigured
×
NEW
45
        }
×
NEW
46
        if ttlValue < ttlMinimum || ttlValue > ttlMaximum {
×
NEW
47
                log.Warnf("TTL value %q must be between [%d, %d]", ttlValue, ttlMinimum, ttlMaximum)
×
NEW
48
                return ttlNotConfigured
×
NEW
49
        }
×
NEW
50
        return endpoint.TTL(ttlValue)
×
51
}
52

53
// TODO: test
54
// TODO: copied from source.go. Refactor to avoid duplication.
55
// parseTTL parses TTL from string, returning duration in seconds.
56
// parseTTL supports both integers like "600" and durations based
57
// on Go Duration like "10m", hence "600" and "10m" represent the same value.
58
//
59
// Note: for durations like "1.5s" the fraction is omitted (resulting in 1 second
60
// for the example).
NEW
61
func parseTTL(s string) (ttlSeconds int64, err error) {
×
NEW
62
        ttlDuration, errDuration := time.ParseDuration(s)
×
NEW
63
        if errDuration != nil {
×
NEW
64
                ttlInt, err := strconv.ParseInt(s, 10, 64)
×
NEW
65
                if err != nil {
×
NEW
66
                        return 0, errDuration
×
NEW
67
                }
×
NEW
68
                return ttlInt, nil
×
69
        }
70

NEW
71
        return int64(ttlDuration.Seconds()), nil
×
72
}
73

NEW
74
func getAliasFromAnnotations(annotations map[string]string) bool {
×
NEW
75
        aliasAnnotation, exists := annotations[aliasAnnotationKey]
×
NEW
76
        return exists && aliasAnnotation == "true"
×
NEW
77
}
×
78

79
// suitableType returns the DNS resource record type suitable for the target.
80
// In this case type A/AAAA for IPs and type CNAME for everything else.
NEW
81
func suitableType(target string) string {
×
NEW
82
        netIP, err := netip.ParseAddr(target)
×
NEW
83
        if err == nil && netIP.Is4() {
×
NEW
84
                return endpoint.RecordTypeA
×
NEW
85
        } else if err == nil && netIP.Is6() {
×
NEW
86
                return endpoint.RecordTypeAAAA
×
NEW
87
        }
×
NEW
88
        return endpoint.RecordTypeCNAME
×
89
}
90

91
// TODO: test
NEW
92
func ParseIngress(ingress string) (namespace, name string, err error) {
×
NEW
93
        parts := strings.Split(ingress, "/")
×
NEW
94
        if len(parts) == 2 {
×
NEW
95
                namespace, name = parts[0], parts[1]
×
NEW
96
        } else if len(parts) == 1 {
×
NEW
97
                name = parts[0]
×
NEW
98
        } else {
×
NEW
99
                err = fmt.Errorf("invalid ingress name (name or namespace/name) found %q", ingress)
×
NEW
100
        }
×
101

NEW
102
        return
×
103
}
104

105
// TODO: test
NEW
106
func SelectorMatchesServiceSelector(selector, svcSelector map[string]string) bool {
×
NEW
107
        for k, v := range selector {
×
NEW
108
                if lbl, ok := svcSelector[k]; !ok || lbl != v {
×
NEW
109
                        return false
×
NEW
110
                }
×
111
        }
NEW
112
        return true
×
113
}
114

115
// TargetsFromTargetAnnotation gets endpoints from optional "target" annotation.
116
// Returns empty endpoints array if none are found.
NEW
117
func TargetsFromTargetAnnotation(annotations map[string]string) endpoint.Targets {
×
NEW
118
        var targets endpoint.Targets
×
NEW
119

×
NEW
120
        // Get the desired hostname of the ingress from the annotation.
×
NEW
121
        targetAnnotation, exists := annotations[targetAnnotationKey]
×
NEW
122
        if exists && targetAnnotation != "" {
×
NEW
123
                // splits the hostname annotation and removes the trailing periods
×
NEW
124
                targetsList := strings.Split(strings.Replace(targetAnnotation, " ", "", -1), ",")
×
NEW
125
                for _, targetHostname := range targetsList {
×
NEW
126
                        targetHostname = strings.TrimSuffix(targetHostname, ".")
×
NEW
127
                        targets = append(targets, targetHostname)
×
NEW
128
                }
×
129
        }
NEW
130
        return targets
×
131
}
132

133
// ParseAnnotationFilter parses an annotation filter string into a labels.Selector.
134
// Returns nil if the annotation filter is invalid.
NEW
135
func ParseAnnotationFilter(annotationFilter string) (labels.Selector, error) {
×
NEW
136
        labelSelector, err := metav1.ParseToLabelSelector(annotationFilter)
×
NEW
137
        if err != nil {
×
NEW
138
                return nil, err
×
NEW
139
        }
×
NEW
140
        selector, err := metav1.LabelSelectorAsSelector(labelSelector)
×
NEW
141
        if err != nil {
×
NEW
142
                return nil, err
×
NEW
143
        }
×
NEW
144
        return selector, nil
×
145
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc