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

k8snetworkplumbingwg / sriov-network-operator / 19227470377

10 Nov 2025 09:48AM UTC coverage: 62.151% (-0.2%) from 62.366%
19227470377

Pull #902

github

web-flow
Merge f9637c189 into 3d1a472a6
Pull Request #902: Create platform and orchestrator packages

319 of 659 new or added lines in 25 files covered. (48.41%)

41 existing lines in 9 files now uncovered.

8772 of 14114 relevant lines covered (62.15%)

0.69 hits per line

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

9.01
/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

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

42
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
43
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
44
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/daemon"
45
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/featuregate"
46
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper"
47
        snolog "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/log"
48
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/platform"
49
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
50
)
51

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

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

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

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

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

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

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

93
        scheme = runtime.NewScheme()
94
)
95

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

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

1✔
111
        // Init logger
1✔
112
        snolog.InitLog()
1✔
113
}
1✔
114

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

122
        vars.ParallelNicConfig = startOpts.parallelNicConfig
×
123
        vars.ManageSoftwareBridges = startOpts.manageSoftwareBridges
×
124
        vars.OVSDBSocketPath = startOpts.ovsSocketPath
×
125

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

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

141
        vars.Scheme = scheme
×
142

×
143
        return nil
×
144
}
145

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

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

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

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

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

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

×
189
        return featureGates, nil
×
190
}
×
191

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

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

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

210
        var config *rest.Config
×
211

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

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

226
        if err != nil {
×
227
                return err
×
228
        }
×
229
        vars.Config = config
×
230

×
231
        // create clients
×
232
        kubeclient := kubernetes.NewForConfigOrDie(config)
×
233
        kClient, err := runtimeclient.New(
×
234
                config,
×
235
                runtimeclient.Options{
×
236
                        Scheme: vars.Scheme})
×
237
        if err != nil {
×
238
                setupLog.Error(err, "couldn't create generic client")
×
239
                os.Exit(1)
×
240
        }
×
241

NEW
242
        nodeInfo, err := kubeclient.CoreV1().Nodes().Get(context.Background(), vars.NodeName, metav1.GetOptions{})
×
243
        if err != nil {
×
244
                setupLog.Error(err, "failed to fetch node state, exiting", "node-name", startOpts.nodeName)
×
245
                return err
×
246
        }
×
247
        // check for a platform
UNCOV
248
        for key, pType := range vars.PlatformsMap {
×
249
                if strings.Contains(strings.ToLower(nodeInfo.Spec.ProviderID), strings.ToLower(key)) {
×
250
                        vars.PlatformType = pType
×
251
                }
×
252
        }
NEW
253
        setupLog.Info("Running on", "platform", vars.PlatformType)
×
NEW
254

×
NEW
255
        // create helpers
×
NEW
256
        hostHelpers, err := helper.NewDefaultHostHelpers()
×
NEW
257
        if err != nil {
×
NEW
258
                setupLog.Error(err, "failed to create hostHelpers")
×
NEW
259
                return err
×
NEW
260
        }
×
261

NEW
262
        plat, err := platform.New(vars.PlatformType, hostHelpers)
×
NEW
263
        if err != nil {
×
NEW
264
                setupLog.Error(err, "failed to create hypervisor")
×
NEW
265
                return err
×
NEW
266
        }
×
267

NEW
268
        eventRecorder := daemon.NewEventRecorder(kClient, kubeclient, scheme)
×
NEW
269
        defer eventRecorder.Shutdown()
×
270

×
271
        // Initial supported nic IDs
×
272
        if err := sriovnetworkv1.InitNicIDMapFromConfigMap(kubeclient, vars.Namespace); err != nil {
×
273
                setupLog.Error(err, "failed to run init NicIdMap")
×
274
                return err
×
275
        }
×
276

277
        operatorConfig, err := getOperatorConfig(kClient)
×
278
        if err != nil {
×
279
                setupLog.Error(err, "Failed to get operator config object")
×
280
                return err
×
281
        }
×
282

283
        // init feature gates
284
        fg, err := initFeatureGates(operatorConfig)
×
285
        if err != nil {
×
286
                setupLog.Error(err, "failed to initialize feature gates")
×
287
                return err
×
288
        }
×
289

290
        // init log level
291
        if err := initLogLevel(operatorConfig); err != nil {
×
292
                setupLog.Error(err, "failed to initialize log level")
×
293
                return err
×
294
        }
×
295

296
        // init disable drain
297
        vars.DisableDrain = operatorConfig.Spec.DisableDrain
×
298

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

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

325
        dm := daemon.New(
×
326
                kClient,
×
327
                hostHelpers,
×
NEW
328
                plat,
×
329
                eventRecorder,
×
NEW
330
                fg)
×
331

×
332
        // Init Daemon configuration on the node
×
NEW
333
        if err = dm.Init(startOpts.disabledPlugins); err != nil {
×
334
                setupLog.Error(err, "unable to initialize daemon")
×
335
                os.Exit(1)
×
336
        }
×
337

338
        // Setup reconcile loop with manager
339
        if err = dm.SetupWithManager(mgr); err != nil {
×
340
                setupLog.Error(err, "unable to setup daemon with manager for SriovNetworkNodeState")
×
341
                os.Exit(1)
×
342
        }
×
343

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

350
        setupLog.Info("Starting Manager")
×
351
        return mgr.Start(stopSignalCh)
×
352
}
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