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

zalando-incubator / stackset-controller / 20348603794

18 Dec 2025 07:21PM UTC coverage: 49.888% (-0.2%) from 50.085%
20348603794

Pull #721

github

mikkeloscar
Improve forward feature

Signed-off-by: Mikkel Oscar Lyderik Larsen <mikkel.larsen@zalando.de>
Pull Request #721: Improve forward feature

14 of 37 new or added lines in 4 files covered. (37.84%)

138 existing lines in 3 files now uncovered.

2662 of 5336 relevant lines covered (49.89%)

0.56 hits per line

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

0.0
/cmd/stackset-controller/main.go
1
package main
2

3
import (
4
        "context"
5
        "net"
6
        "net/http"
7
        "net/url"
8
        "os"
9
        "os/signal"
10
        "syscall"
11
        "time"
12

13
        "github.com/alecthomas/kingpin/v2"
14
        "github.com/prometheus/client_golang/prometheus"
15
        "github.com/prometheus/client_golang/prometheus/promhttp"
16
        log "github.com/sirupsen/logrus"
17
        "github.com/zalando-incubator/stackset-controller/controller"
18
        "github.com/zalando-incubator/stackset-controller/pkg/clientset"
19
        "github.com/zalando-incubator/stackset-controller/pkg/traffic"
20
        corev1 "k8s.io/api/core/v1"
21
        "k8s.io/client-go/rest"
22
        "k8s.io/client-go/tools/clientcmd"
23
        "k8s.io/client-go/transport"
24
)
25

26
const (
27
        defaultInterval               = "10s"
28
        defaultIngressSourceSwitchTTL = "5m"
29
        defaultMetricsAddress         = ":7979"
30
        defaultClientGOTimeout        = 30 * time.Second
31
        defaultReconcileWorkers       = "10"
32
)
33

34
var (
35
        config struct {
36
                Debug                       bool
37
                Interval                    time.Duration
38
                APIServer                   *url.URL
39
                Namespace                   string
40
                MetricsAddress              string
41
                ClusterDomains              []string
42
                NoTrafficScaledownTTL       time.Duration
43
                ControllerID                string
44
                BackendWeightsAnnotationKey string
45
                RouteGroupSupportEnabled    bool
46
                SyncIngressAnnotations      []string
47
                ReconcileWorkers            int
48
                ConfigMapSupportEnabled     bool
49
                SecretSupportEnabled        bool
50
                PCSSupportEnabled           bool
51
        }
52
)
53

54
func main() {
×
55
        kingpin.Flag("debug", "Enable debug logging.").BoolVar(&config.Debug)
×
56
        kingpin.Flag("interval", "Interval between syncing stacksets.").
×
57
                Default(defaultInterval).DurationVar(&config.Interval)
×
58
        kingpin.Flag("apiserver", "API server url.").URLVar(&config.APIServer)
×
59
        kingpin.Flag("namespace", "Limit scope to a particular namespace.").Default(corev1.NamespaceAll).StringVar(&config.Namespace)
×
60
        kingpin.Flag("metrics-address", "defines where to serve metrics").Default(defaultMetricsAddress).StringVar(&config.MetricsAddress)
×
61
        kingpin.Flag("controller-id", "ID of the controller used to determine ownership of StackSet resources").StringVar(&config.ControllerID)
×
62
        kingpin.Flag("reconcile-workers", "The amount of stacksets to reconcile in parallel at a time.").
×
63
                Default(defaultReconcileWorkers).IntVar(&config.ReconcileWorkers)
×
64
        kingpin.Flag("backend-weights-key", "Backend weights annotation key the controller will use to set current traffic values").Default(traffic.DefaultBackendWeightsAnnotationKey).StringVar(&config.BackendWeightsAnnotationKey)
×
65
        kingpin.Flag("cluster-domain", "Main domains of the cluster, used for generating Stack Ingress hostnames").Envar("CLUSTER_DOMAIN").Required().StringsVar(&config.ClusterDomains)
×
66
        kingpin.Flag("enable-routegroup-support", "Enable support for RouteGroups on StackSets.").Default("false").BoolVar(&config.RouteGroupSupportEnabled)
×
67
        kingpin.Flag(
×
68
                "sync-ingress-annotation",
×
69
                "Ingress/RouteGroup annotation to propagate to all traffic segments.",
×
70
        ).StringsVar(&config.SyncIngressAnnotations)
×
71
        kingpin.Flag("enable-configmap-support", "Enable support for ConfigMaps on StackSets.").Default("false").BoolVar(&config.ConfigMapSupportEnabled)
×
72
        kingpin.Flag("enable-secret-support", "Enable support for Secrets on StackSets.").Default("false").BoolVar(&config.SecretSupportEnabled)
×
73
        kingpin.Flag("enable-pcs-support", "Enable support for PlatformCredentialsSet on StackSets.").Default("false").BoolVar(&config.PCSSupportEnabled)
×
74
        kingpin.Parse()
×
75

×
76
        if config.Debug {
×
77
                log.SetLevel(log.DebugLevel)
×
78
        }
×
79

UNCOV
80
        stackSetConfig := controller.StackSetConfig{
×
81
                Namespace:    config.Namespace,
×
82
                ControllerID: config.ControllerID,
×
83

×
84
                ClusterDomains:              config.ClusterDomains,
×
85
                BackendWeightsAnnotationKey: config.BackendWeightsAnnotationKey,
×
86
                SyncIngressAnnotations:      config.SyncIngressAnnotations,
×
87

×
88
                ReconcileWorkers: config.ReconcileWorkers,
×
89
                Interval:         config.Interval,
×
90

×
91
                RouteGroupSupportEnabled: config.RouteGroupSupportEnabled,
×
92
                ConfigMapSupportEnabled:  config.ConfigMapSupportEnabled,
×
93
                SecretSupportEnabled:     config.SecretSupportEnabled,
×
94
                PcsSupportEnabled:        config.PCSSupportEnabled,
×
95
        }
×
96

×
97
        ctx, cancel := context.WithCancel(context.Background())
×
98
        kubeConfig, err := configureKubeConfig(config.APIServer, defaultClientGOTimeout, ctx.Done())
×
99
        if err != nil {
×
100
                log.Fatalf("Failed to setup Kubernetes config: %v", err)
×
101
        }
×
102

103
        client, err := clientset.NewForConfig(kubeConfig)
×
UNCOV
104
        if err != nil {
×
105
                log.Fatalf("Failed to initialize Kubernetes client: %v", err)
×
106
        }
×
107

108
        controller, err := controller.NewStackSetController(
×
UNCOV
109
                client,
×
110
                prometheus.DefaultRegisterer,
×
111
                stackSetConfig,
×
112
        )
×
113
        if err != nil {
×
114
                log.Fatalf("Failed to create Stackset controller: %v", err)
×
115
        }
×
116

117
        go handleSigterm(cancel)
×
UNCOV
118
        go serveMetrics(config.MetricsAddress)
×
119
        err = controller.Run(ctx)
×
120
        if err != nil {
×
121
                cancel()
×
122
                log.Fatalf("Failed to run controller: %v", err)
×
123
        }
×
124
}
125

126
// handleSigterm handles SIGTERM signal sent to the process.
UNCOV
127
func handleSigterm(cancelFunc func()) {
×
UNCOV
128
        signals := make(chan os.Signal, 1)
×
129
        signal.Notify(signals, syscall.SIGTERM)
×
130
        <-signals
×
131
        log.Info("Received Term signal. Terminating...")
×
132
        cancelFunc()
×
133
}
×
134

135
// configureKubeConfig configures a kubeconfig.
UNCOV
136
func configureKubeConfig(apiServerURL *url.URL, timeout time.Duration, stopCh <-chan struct{}) (*rest.Config, error) {
×
UNCOV
137
        tr := &http.Transport{
×
138
                DialContext: (&net.Dialer{
×
139
                        Timeout:   timeout,
×
140
                        KeepAlive: 30 * time.Second,
×
141
                        DualStack: false, // K8s do not work well with IPv6
×
142
                }).DialContext,
×
143
                TLSHandshakeTimeout:   timeout,
×
144
                ResponseHeaderTimeout: 10 * time.Second,
×
145
                MaxIdleConns:          10,
×
146
                MaxIdleConnsPerHost:   2,
×
147
                IdleConnTimeout:       20 * time.Second,
×
148
        }
×
149

×
150
        // We need this to reliably fade on DNS change, which is right
×
151
        // now not fixed with IdleConnTimeout in the http.Transport.
×
152
        // https://github.com/golang/go/issues/23427
×
153
        go func(d time.Duration) {
×
154
                for {
×
155
                        select {
×
156
                        case <-time.After(d):
×
157
                                tr.CloseIdleConnections()
×
158
                        case <-stopCh:
×
159
                                return
×
160
                        }
161
                }
162
        }(20 * time.Second)
163

UNCOV
164
        if apiServerURL != nil {
×
UNCOV
165
                return &rest.Config{
×
166
                        Host:      apiServerURL.String(),
×
167
                        Timeout:   timeout,
×
168
                        Transport: tr,
×
169
                        QPS:       100.0,
×
170
                        Burst:     500,
×
171
                }, nil
×
172
        }
×
173

174
        // Try in-cluster config
UNCOV
175
        config, err := rest.InClusterConfig()
×
NEW
176
        if err == rest.ErrNotInCluster {
×
NEW
177
                // fall back to kubeconfig
×
NEW
178
                kubeconfig := os.Getenv("KUBECONFIG")
×
NEW
179
                if kubeconfig == "" {
×
NEW
UNCOV
180
                        kubeconfig = os.ExpandEnv("${HOME}/.kube/config")
×
NEW
UNCOV
181
                }
×
NEW
182
                return clientcmd.BuildConfigFromFlags("", kubeconfig)
×
183
        }
184
        if err != nil {
×
185
                return nil, err
×
186
        }
×
187

188
        // patch TLS config
189
        restTransportConfig, err := config.TransportConfig()
×
190
        if err != nil {
×
191
                return nil, err
×
192
        }
×
193
        restTLSConfig, err := transport.TLSConfigFor(restTransportConfig)
×
194
        if err != nil {
×
195
                return nil, err
×
196
        }
×
197
        tr.TLSClientConfig = restTLSConfig
×
198

×
UNCOV
199
        config.Timeout = timeout
×
UNCOV
200
        config.Transport = tr
×
UNCOV
201
        config.QPS = 100.0
×
202
        config.Burst = 500
×
203
        // disable TLSClientConfig to make the custom Transport work
×
204
        config.TLSClientConfig = rest.TLSClientConfig{}
×
205
        return config, nil
×
206
}
207

208
// gather go metrics
UNCOV
209
func serveMetrics(address string) {
×
UNCOV
210
        http.Handle("/metrics", promhttp.Handler())
×
UNCOV
211
        log.Fatal(http.ListenAndServe(address, nil))
×
UNCOV
212
}
×
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

© 2025 Coveralls, Inc