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

k8snetworkplumbingwg / sriov-network-operator / 13526205039

25 Feb 2025 04:34PM UTC coverage: 48.882% (+0.9%) from 48.008%
13526205039

Pull #788

github

web-flow
Merge 3e6b91cf3 into d7c9458e0
Pull Request #788: Daemon redesign - using controller-runtime

273 of 615 new or added lines in 18 files covered. (44.39%)

199 existing lines in 12 files now uncovered.

7324 of 14983 relevant lines covered (48.88%)

0.54 hits per line

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

8.94
/cmd/sriov-network-config-daemon/start.go
1
/*
2
Copyright 2023.
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
package main
17

18
import (
19
        "context"
20
        "fmt"
21
        "net/url"
22
        "os"
23
        "strings"
24
        "time"
25

26
        ocpconfigapi "github.com/openshift/api/config/v1"
27
        "github.com/spf13/cobra"
28
        v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
        "k8s.io/apimachinery/pkg/fields"
30
        "k8s.io/apimachinery/pkg/runtime"
31
        "k8s.io/apimachinery/pkg/types"
32
        utilruntime "k8s.io/apimachinery/pkg/util/runtime"
33
        "k8s.io/client-go/kubernetes"
34
        clientgoscheme "k8s.io/client-go/kubernetes/scheme"
35
        "k8s.io/client-go/rest"
36
        "k8s.io/client-go/tools/clientcmd"
37
        ctrl "sigs.k8s.io/controller-runtime"
38
        "sigs.k8s.io/controller-runtime/pkg/cache"
39
        runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
40
        "sigs.k8s.io/controller-runtime/pkg/log"
41
        "sigs.k8s.io/controller-runtime/pkg/metrics/server"
42

43
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
44
        snclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned"
45
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
46
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/daemon"
47
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
48
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper"
49
        snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log"
50
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platforms"
51
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
52
)
53

54
// stringList is a list of strings, implements pflag.Value interface
55
type stringList []string
56

57
func (sl *stringList) String() string {
1✔
58
        return strings.Join(*sl, ",")
1✔
59
}
1✔
60

61
func (sl *stringList) Set(arg string) error {
×
62
        elems := strings.Split(arg, ",")
×
63

×
64
        for _, elem := range elems {
×
65
                if len(elem) == 0 {
×
66
                        return fmt.Errorf("empty plugin name")
×
67
                }
×
68
                *sl = append(*sl, elem)
×
69
        }
70
        return nil
×
71
}
72

73
func (sl *stringList) Type() string {
×
74
        return "CommaSeparatedString"
×
75
}
×
76

77
var (
78
        startCmd = &cobra.Command{
79
                Use:   "start",
80
                Short: "Starts SR-IOV Network Config Daemon",
81
                Long:  "",
82
                RunE:  runStartCmd,
83
        }
84

85
        startOpts struct {
86
                kubeconfig            string
87
                nodeName              string
88
                systemd               bool
89
                disabledPlugins       stringList
90
                parallelNicConfig     bool
91
                manageSoftwareBridges bool
92
                ovsSocketPath         string
93
        }
94

95
        scheme = runtime.NewScheme()
96
)
97

98
func init() {
1✔
99
        rootCmd.AddCommand(startCmd)
1✔
100
        startCmd.PersistentFlags().StringVar(&startOpts.kubeconfig, "kubeconfig", "", "Kubeconfig file to access a remote cluster (testing only)")
1✔
101
        startCmd.PersistentFlags().StringVar(&startOpts.nodeName, "node-name", "", "kubernetes node name daemon is managing")
1✔
102
        startCmd.PersistentFlags().BoolVar(&startOpts.systemd, "use-systemd-service", false, "use config daemon in systemd mode")
1✔
103
        startCmd.PersistentFlags().VarP(&startOpts.disabledPlugins, "disable-plugins", "", "comma-separated list of plugins to disable")
1✔
104
        startCmd.PersistentFlags().BoolVar(&startOpts.parallelNicConfig, "parallel-nic-config", false, "perform NIC configuration in parallel")
1✔
105
        startCmd.PersistentFlags().BoolVar(&startOpts.manageSoftwareBridges, "manage-software-bridges", false, "enable management of software bridges")
1✔
106
        startCmd.PersistentFlags().StringVar(&startOpts.ovsSocketPath, "ovs-socket-path", vars.OVSDBSocketPath, "path for OVSDB socket")
1✔
107

1✔
108
        // Init Scheme
1✔
109
        utilruntime.Must(clientgoscheme.AddToScheme(scheme))
1✔
110
        utilruntime.Must(sriovnetworkv1.AddToScheme(scheme))
1✔
111
        utilruntime.Must(ocpconfigapi.AddToScheme(scheme))
1✔
112

1✔
113
        // Init logger
1✔
114
        snolog.InitLog()
1✔
115
}
1✔
116

NEW
117
func configGlobalVariables() error {
×
118
        // Mark that we are running inside a container
×
119
        vars.UsingSystemdMode = false
×
120
        if startOpts.systemd {
×
121
                vars.UsingSystemdMode = true
×
122
        }
×
123

124
        vars.ParallelNicConfig = startOpts.parallelNicConfig
×
125
        vars.ManageSoftwareBridges = startOpts.manageSoftwareBridges
×
126
        vars.OVSDBSocketPath = startOpts.ovsSocketPath
×
127

×
128
        if startOpts.nodeName == "" {
×
129
                name, ok := os.LookupEnv("NODE_NAME")
×
130
                if !ok || name == "" {
×
131
                        return fmt.Errorf("node-name is required")
×
132
                }
×
133
                startOpts.nodeName = name
×
134
        }
135
        vars.NodeName = startOpts.nodeName
×
136

×
137
        for _, p := range startOpts.disabledPlugins {
×
138
                if _, ok := vars.DisableablePlugins[p]; !ok {
×
139
                        return fmt.Errorf("%s plugin cannot be disabled", p)
×
140
                }
×
141
        }
142

NEW
143
        vars.Scheme = scheme
×
NEW
144

×
NEW
145
        return nil
×
146
}
147

NEW
148
func useKubeletKubeConfig() {
×
NEW
149
        fnLogger := log.Log.WithName("sriov-network-config-daemon")
×
NEW
150

×
NEW
151
        kubeconfig, err := clientcmd.LoadFromFile("/host/etc/kubernetes/kubeconfig")
×
NEW
152
        if err != nil {
×
NEW
153
                fnLogger.Error(err, "failed to load kubelet kubeconfig")
×
NEW
154
        }
×
NEW
155
        clusterName := kubeconfig.Contexts[kubeconfig.CurrentContext].Cluster
×
NEW
156
        apiURL := kubeconfig.Clusters[clusterName].Server
×
NEW
157

×
NEW
158
        urlPath, err := url.Parse(apiURL)
×
NEW
159
        if err != nil {
×
NEW
160
                fnLogger.Error(err, "failed to parse api url from kubelet kubeconfig")
×
NEW
161
        }
×
162

163
        // The kubernetes in-cluster functions don't let you override the apiserver
164
        // directly; gotta "pass" it via environment vars.
NEW
165
        fnLogger.V(0).Info("overriding kubernetes api", "new-url", apiURL)
×
NEW
166
        err = os.Setenv("KUBERNETES_SERVICE_HOST", urlPath.Hostname())
×
NEW
167
        if err != nil {
×
NEW
168
                fnLogger.Error(err, "failed to set KUBERNETES_SERVICE_HOST environment variable")
×
NEW
169
        }
×
NEW
170
        err = os.Setenv("KUBERNETES_SERVICE_PORT", urlPath.Port())
×
NEW
171
        if err != nil {
×
NEW
172
                fnLogger.Error(err, "failed to set KUBERNETES_SERVICE_PORT environment variable")
×
NEW
173
        }
×
174
}
175

NEW
176
func getOperatorConfig(kClient runtimeclient.Client) (*sriovnetworkv1.SriovOperatorConfig, error) {
×
NEW
177
        defaultConfig := &sriovnetworkv1.SriovOperatorConfig{}
×
NEW
178
        err := kClient.Get(context.Background(), types.NamespacedName{Namespace: vars.Namespace, Name: consts.DefaultConfigName}, defaultConfig)
×
NEW
179
        if err != nil {
×
NEW
180
                return nil, err
×
NEW
181
        }
×
NEW
182
        return defaultConfig, nil
×
183
}
184

NEW
185
func initFeatureGates(defaultConfig *sriovnetworkv1.SriovOperatorConfig) (featuregate.FeatureGate, error) {
×
NEW
186
        fnLogger := log.Log.WithName("initFeatureGates")
×
NEW
187
        featureGates := featuregate.New()
×
NEW
188
        featureGates.Init(defaultConfig.Spec.FeatureGates)
×
NEW
189
        fnLogger.Info("Enabled featureGates", "featureGates", featureGates.String())
×
190

×
NEW
191
        return featureGates, nil
×
NEW
192
}
×
193

NEW
194
func initLogLevel(defaultConfig *sriovnetworkv1.SriovOperatorConfig) error {
×
NEW
195
        fnLogger := log.Log.WithName("initLogLevel")
×
NEW
196
        snolog.SetLogLevel(defaultConfig.Spec.LogLevel)
×
NEW
197
        fnLogger.V(2).Info("logLevel sets", "logLevel", defaultConfig.Spec.LogLevel)
×
NEW
198
        return nil
×
NEW
199
}
×
200

NEW
201
func runStartCmd(cmd *cobra.Command, args []string) error {
×
NEW
202
        setupLog := log.Log.WithName("sriov-network-config-daemon")
×
NEW
203
        stopSignalCh := ctrl.SetupSignalHandler()
×
204

×
NEW
205
        // Load global variables
×
NEW
206
        err := configGlobalVariables()
×
NEW
207
        if err != nil {
×
NEW
208
                setupLog.Error(err, "unable to config global variables")
×
NEW
209
                return err
×
NEW
210
        }
×
211

212
        var config *rest.Config
×
213

×
214
        // On openshift we use the kubeconfig from kubelet on the node where the daemon is running
×
215
        // this allow us to improve security as every daemon has access only to its own node
×
216
        if vars.ClusterType == consts.ClusterTypeOpenshift {
×
NEW
217
                useKubeletKubeConfig()
×
218
        }
×
219

220
        kubeconfig := os.Getenv("KUBECONFIG")
×
221
        if kubeconfig != "" {
×
222
                config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
×
223
        } else {
×
224
                // creates the in-cluster config
×
225
                config, err = rest.InClusterConfig()
×
226
        }
×
227

228
        if err != nil {
×
229
                return err
×
230
        }
×
UNCOV
231
        vars.Config = config
×
NEW
232
        config.Timeout = 5 * time.Second
×
233

×
NEW
234
        // create helpers
×
NEW
235
        hostHelpers, err := helper.NewDefaultHostHelpers()
×
236
        if err != nil {
×
NEW
237
                setupLog.Error(err, "failed to create hostHelpers")
×
238
                return err
×
239
        }
×
240

NEW
241
        platformHelper, err := platforms.NewDefaultPlatformHelper()
×
242
        if err != nil {
×
NEW
243
                setupLog.Error(err, "failed to create platformHelper")
×
244
                return err
×
245
        }
×
246

247
        // create clients
NEW
248
        snclient := snclientset.NewForConfigOrDie(config)
×
NEW
249
        kubeclient := kubernetes.NewForConfigOrDie(config)
×
NEW
250
        kClient, err := runtimeclient.New(
×
NEW
251
                config,
×
NEW
252
                runtimeclient.Options{
×
NEW
253
                        Scheme: vars.Scheme})
×
254
        if err != nil {
×
NEW
255
                setupLog.Error(err, "couldn't create generic client")
×
NEW
256
                os.Exit(1)
×
UNCOV
257
        }
×
258

NEW
259
        eventRecorder := daemon.NewEventRecorder(snclient, kubeclient, scheme)
×
NEW
260
        defer eventRecorder.Shutdown()
×
NEW
261

×
NEW
262
        nodeInfo, err := kubeclient.CoreV1().Nodes().Get(context.Background(), vars.NodeName, v1.GetOptions{})
×
263
        if err != nil {
×
NEW
264
                setupLog.Error(err, "failed to fetch node state, exiting", "node-name", startOpts.nodeName)
×
265
                return err
×
266
        }
×
267

268
        // check for platform
NEW
269
        for key, pType := range vars.PlatformsMap {
×
NEW
270
                if strings.Contains(strings.ToLower(nodeInfo.Spec.ProviderID), strings.ToLower(key)) {
×
NEW
271
                        vars.PlatformType = pType
×
NEW
272
                }
×
273
        }
NEW
274
        setupLog.Info("Running on", "platform", vars.PlatformType.String())
×
275

×
NEW
276
        // Initial supported nic IDs
×
NEW
277
        if err := sriovnetworkv1.InitNicIDMapFromConfigMap(kubeclient, vars.Namespace); err != nil {
×
NEW
278
                setupLog.Error(err, "failed to run init NicIdMap")
×
279
                return err
×
280
        }
×
281

NEW
282
        operatorConfig, err := getOperatorConfig(kClient)
×
283
        if err != nil {
×
NEW
284
                setupLog.Error(err, "Failed to get operator config object")
×
285
                return err
×
286
        }
×
287

288
        // init feature gates
NEW
289
        fg, err := initFeatureGates(operatorConfig)
×
NEW
290
        if err != nil {
×
NEW
291
                setupLog.Error(err, "failed to initialize feature gates")
×
292
                return err
×
293
        }
×
294

295
        // init log level
NEW
296
        if err := initLogLevel(operatorConfig); err != nil {
×
NEW
297
                setupLog.Error(err, "failed to initialize log level")
×
298
                return err
×
299
        }
×
300

301
        // init disable drain
NEW
302
        vars.DisableDrain = operatorConfig.Spec.DisableDrain
×
303

×
NEW
304
        // Init manager
×
NEW
305
        setupLog.V(0).Info("Starting SR-IOV Network Config Daemon")
×
NEW
306
        nodeStateSelector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s,metadata.namespace=%s", vars.NodeName, vars.Namespace))
×
307
        if err != nil {
×
NEW
308
                setupLog.Error(err, "failed to parse sriovNetworkNodeState name selector")
×
309
                return err
×
310
        }
×
NEW
311
        operatorConfigSelector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s,metadata.namespace=%s", consts.DefaultConfigName, vars.Namespace))
×
312
        if err != nil {
×
NEW
313
                setupLog.Error(err, "failed to parse sriovOperatorConfig name selector")
×
314
                return err
×
315
        }
×
316

NEW
317
        mgr, err := ctrl.NewManager(vars.Config, ctrl.Options{
×
NEW
318
                Scheme:  vars.Scheme,
×
NEW
319
                Metrics: server.Options{BindAddress: "0"}, // disable metrics server for now as the daemon runs with hostNetwork
×
NEW
320
                Cache: cache.Options{ // cache only the SriovNetworkNodeState with the node name
×
NEW
321
                        ByObject: map[runtimeclient.Object]cache.ByObject{
×
NEW
322
                                &sriovnetworkv1.SriovNetworkNodeState{}: {Field: nodeStateSelector},
×
NEW
323
                                &sriovnetworkv1.SriovOperatorConfig{}:   {Field: operatorConfigSelector}}},
×
NEW
324
        })
×
NEW
325
        if err != nil {
×
NEW
326
                setupLog.Error(err, "unable to create manager")
×
NEW
327
                os.Exit(1)
×
NEW
328
        }
×
329

NEW
330
        dm := daemon.New(
×
331
                kClient,
×
332
                hostHelpers,
×
333
                platformHelper,
×
334
                eventRecorder,
×
NEW
335
                fg,
×
NEW
336
                startOpts.disabledPlugins)
×
NEW
337

×
NEW
338
        // Init Daemon configuration on the node
×
NEW
339
        if err = dm.Init(); err != nil {
×
NEW
340
                setupLog.Error(err, "unable to initialize daemon")
×
NEW
341
                os.Exit(1)
×
NEW
342
        }
×
343

344
        // Setup reconcile loop with manager
NEW
345
        if err = dm.SetupWithManager(mgr); err != nil {
×
NEW
346
                setupLog.Error(err, "unable to setup daemon with manager for SriovNetworkNodeState")
×
NEW
347
                os.Exit(1)
×
348
        }
×
349

350
        // Setup reconcile loop with manager
NEW
351
        if err = daemon.NewOperatorConfigNodeReconcile(kClient).SetupWithManager(mgr); err != nil {
×
NEW
352
                setupLog.Error(err, "unable to create setup daemon manager for OperatorConfig")
×
NEW
353
                os.Exit(1)
×
354
        }
×
355

NEW
356
        setupLog.Info("Starting Manager")
×
NEW
357
        return mgr.Start(stopSignalCh)
×
358
}
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