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

k8snetworkplumbingwg / sriov-network-operator / 11341895365

15 Oct 2024 08:08AM UTC coverage: 44.967% (-0.05%) from 45.017%
11341895365

Pull #792

github

web-flow
Merge 9c8ed6e3c into 8fe7a5e00
Pull Request #792: Clean Systemd files on exit

10 of 52 new or added lines in 2 files covered. (19.23%)

1 existing line in 1 file now uncovered.

6669 of 14831 relevant lines covered (44.97%)

0.5 hits per line

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

5.39
/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"
22
        "net/url"
23
        "os"
24
        "os/signal"
25
        "strings"
26
        "syscall"
27
        "time"
28

29
        "github.com/spf13/cobra"
30
        v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31
        "k8s.io/apimachinery/pkg/types"
32
        "k8s.io/client-go/kubernetes"
33
        "k8s.io/client-go/kubernetes/scheme"
34
        "k8s.io/client-go/rest"
35
        "k8s.io/client-go/tools/clientcmd"
36
        "k8s.io/client-go/util/connrotation"
37
        "sigs.k8s.io/controller-runtime/pkg/client"
38
        "sigs.k8s.io/controller-runtime/pkg/log"
39

40
        configv1 "github.com/openshift/api/config/v1"
41
        mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
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

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

107
func runStartCmd(cmd *cobra.Command, args []string) error {
×
108
        // init logger
×
109
        snolog.InitLog()
×
110
        setupLog := log.Log.WithName("sriov-network-config-daemon")
×
111

×
112
        // Mark that we are running inside a container
×
113
        vars.UsingSystemdMode = false
×
114
        if startOpts.systemd {
×
115
                vars.UsingSystemdMode = true
×
116
        }
×
117

118
        vars.ParallelNicConfig = startOpts.parallelNicConfig
×
119
        vars.ManageSoftwareBridges = startOpts.manageSoftwareBridges
×
120
        vars.OVSDBSocketPath = startOpts.ovsSocketPath
×
121

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

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

137
        // This channel is used to ensure all spawned goroutines exit when we exit.
138
        stopCh := make(chan struct{})
×
139

×
140
        // This channel is used to signal Run() something failed and to jump ship.
×
141
        // It's purely a chan<- in the Daemon struct for goroutines to write to, and
×
142
        // a <-chan in Run() for the main thread to listen on.
×
143
        exitCh := make(chan error)
×
144
        defer close(exitCh)
×
145

×
146
        // This channel is to make sure main thread will wait until the writer finish
×
147
        // to report lastSyncError in SriovNetworkNodeState object.
×
148
        syncCh := make(chan struct{})
×
149
        defer close(syncCh)
×
150

×
151
        refreshCh := make(chan daemon.Message)
×
152
        defer close(refreshCh)
×
153

×
154
        var config *rest.Config
×
155
        var err error
×
156

×
157
        // On openshift we use the kubeconfig from kubelet on the node where the daemon is running
×
158
        // this allow us to improve security as every daemon has access only to its own node
×
159
        if vars.ClusterType == consts.ClusterTypeOpenshift {
×
160
                kubeconfig, err := clientcmd.LoadFromFile("/host/etc/kubernetes/kubeconfig")
×
161
                if err != nil {
×
162
                        setupLog.Error(err, "failed to load kubelet kubeconfig")
×
163
                }
×
164
                clusterName := kubeconfig.Contexts[kubeconfig.CurrentContext].Cluster
×
165
                apiURL := kubeconfig.Clusters[clusterName].Server
×
166

×
167
                urlPath, err := url.Parse(apiURL)
×
168
                if err != nil {
×
169
                        setupLog.Error(err, "failed to parse api url from kubelet kubeconfig")
×
170
                }
×
171

172
                // The kubernetes in-cluster functions don't let you override the apiserver
173
                // directly; gotta "pass" it via environment vars.
174
                setupLog.V(0).Info("overriding kubernetes api", "new-url", apiURL)
×
175
                err = os.Setenv("KUBERNETES_SERVICE_HOST", urlPath.Hostname())
×
176
                if err != nil {
×
177
                        setupLog.Error(err, "failed to set KUBERNETES_SERVICE_HOST environment variable")
×
178
                }
×
179
                err = os.Setenv("KUBERNETES_SERVICE_PORT", urlPath.Port())
×
180
                if err != nil {
×
181
                        setupLog.Error(err, "failed to set KUBERNETES_SERVICE_PORT environment variable")
×
182
                }
×
183
        }
184

185
        kubeconfig := os.Getenv("KUBECONFIG")
×
186
        if kubeconfig != "" {
×
187
                config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
×
188
        } else {
×
189
                // creates the in-cluster config
×
190
                config, err = rest.InClusterConfig()
×
191
        }
×
192

193
        if err != nil {
×
194
                return err
×
195
        }
×
196

197
        vars.Config = config
×
198
        vars.Scheme = scheme.Scheme
×
199

×
200
        closeAllConns, err := updateDialer(config)
×
201
        if err != nil {
×
202
                return err
×
203
        }
×
204

205
        err = sriovnetworkv1.AddToScheme(scheme.Scheme)
×
206
        if err != nil {
×
207
                setupLog.Error(err, "failed to load sriov network CRDs to scheme")
×
208
                return err
×
209
        }
×
210

211
        err = mcfgv1.AddToScheme(scheme.Scheme)
×
212
        if err != nil {
×
213
                setupLog.Error(err, "failed to load machine config CRDs to scheme")
×
214
                return err
×
215
        }
×
216

217
        err = configv1.Install(scheme.Scheme)
×
218
        if err != nil {
×
219
                setupLog.Error(err, "failed to load openshift config CRDs to scheme")
×
220
                return err
×
221
        }
×
222

223
        kClient, err := client.New(config, client.Options{Scheme: scheme.Scheme})
×
224
        if err != nil {
×
225
                setupLog.Error(err, "couldn't create client")
×
226
                os.Exit(1)
×
227
        }
×
228

229
        snclient := snclientset.NewForConfigOrDie(config)
×
230
        kubeclient := kubernetes.NewForConfigOrDie(config)
×
231

×
232
        hostHelpers, err := helper.NewDefaultHostHelpers()
×
233
        if err != nil {
×
234
                setupLog.Error(err, "failed to create hostHelpers")
×
235
                return err
×
236
        }
×
237

238
        platformHelper, err := platforms.NewDefaultPlatformHelper()
×
239
        if err != nil {
×
240
                setupLog.Error(err, "failed to create platformHelper")
×
241
                return err
×
242
        }
×
243

244
        config.Timeout = 5 * time.Second
×
245
        writerclient := snclientset.NewForConfigOrDie(config)
×
246

×
247
        eventRecorder := daemon.NewEventRecorder(writerclient, kubeclient)
×
248
        defer eventRecorder.Shutdown()
×
249

×
250
        setupLog.V(0).Info("starting node writer")
×
251
        nodeWriter := daemon.NewNodeStateStatusWriter(writerclient,
×
252
                closeAllConns,
×
253
                eventRecorder,
×
254
                hostHelpers,
×
255
                platformHelper)
×
256

×
257
        nodeInfo, err := kubeclient.CoreV1().Nodes().Get(context.Background(), startOpts.nodeName, v1.GetOptions{})
×
258
        if err == nil {
×
259
                for key, pType := range vars.PlatformsMap {
×
260
                        if strings.Contains(strings.ToLower(nodeInfo.Spec.ProviderID), strings.ToLower(key)) {
×
261
                                vars.PlatformType = pType
×
262
                        }
×
263
                }
264
        } else {
×
265
                setupLog.Error(err, "failed to fetch node state, exiting", "node-name", startOpts.nodeName)
×
266
                return err
×
267
        }
×
268
        setupLog.Info("Running on", "platform", vars.PlatformType.String())
×
269

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

275
        eventRecorder.SendEvent("ConfigDaemonStart", "Config Daemon starting")
×
276

×
277
        // block the deamon process until nodeWriter finish first its run
×
278
        err = nodeWriter.RunOnce()
×
279
        if err != nil {
×
280
                setupLog.Error(err, "failed to run writer")
×
281
                return err
×
282
        }
×
283
        go nodeWriter.Run(stopCh, refreshCh, syncCh)
×
284

×
285
        // Init feature gates once to prevent race conditions.
×
286
        defaultConfig := &sriovnetworkv1.SriovOperatorConfig{}
×
287
        err = kClient.Get(context.Background(), types.NamespacedName{Namespace: vars.Namespace, Name: consts.DefaultConfigName}, defaultConfig)
×
288
        if err != nil {
×
289
                log.Log.Error(err, "Failed to get default SriovOperatorConfig object")
×
NEW
290
                close(stopCh)
×
291
                return err
×
292
        }
×
293
        featureGates := featuregate.New()
×
294
        featureGates.Init(defaultConfig.Spec.FeatureGates)
×
295
        vars.MlxPluginFwReset = featureGates.IsEnabled(consts.MellanoxFirmwareResetFeatureGate)
×
296
        log.Log.Info("Enabled featureGates", "featureGates", featureGates.String())
×
297

×
298
        setupLog.V(0).Info("Starting SriovNetworkConfigDaemon")
×
NEW
299

×
NEW
300
        // create a signal channel to catch interrupts and gracefully shutdown the daemon
×
NEW
301
        sigc := make(chan os.Signal, 1)
×
NEW
302
        signal.Notify(sigc, os.Interrupt)
×
NEW
303
        signal.Notify(sigc, syscall.SIGTERM)
×
NEW
304

×
NEW
305
        errChan := make(chan error)
×
NEW
306
        defer close(errChan)
×
NEW
307
        go func() {
×
NEW
308
                errChan <- daemon.New(
×
NEW
309
                        kClient,
×
NEW
310
                        snclient,
×
NEW
311
                        kubeclient,
×
NEW
312
                        hostHelpers,
×
NEW
313
                        platformHelper,
×
NEW
314
                        exitCh,
×
NEW
315
                        stopCh,
×
NEW
316
                        syncCh,
×
NEW
317
                        refreshCh,
×
NEW
318
                        eventRecorder,
×
NEW
319
                        featureGates,
×
NEW
320
                        startOpts.disabledPlugins,
×
NEW
321
                ).Run(stopCh, exitCh)
×
NEW
322
        }()
×
323

NEW
324
        select {
×
NEW
325
        case err := <-errChan:
×
NEW
326
                // daemon has exited, close the stop channel and return the error
×
NEW
327
                close(stopCh)
×
NEW
328
                return err
×
NEW
329
        case <-sigc:
×
NEW
330
                // signal received, close the stop channel and wait for the daemon to exit
×
NEW
331
                close(stopCh)
×
NEW
332
                if err := <-errChan; err != nil {
×
NEW
333
                        return err
×
NEW
334
                }
×
335
        }
336
        setupLog.V(0).Info("Shutting down SriovNetworkConfigDaemon")
×
NEW
337
        return nil
×
338
}
339

340
// updateDialer instruments a restconfig with a dial. the returned function allows forcefully closing all active connections.
341
func updateDialer(clientConfig *rest.Config) (func(), error) {
×
342
        if clientConfig.Transport != nil || clientConfig.Dial != nil {
×
343
                return nil, fmt.Errorf("there is already a transport or dialer configured")
×
344
        }
×
345
        f := &net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}
×
346
        d := connrotation.NewDialer(f.DialContext)
×
347
        clientConfig.Dial = d.DialContext
×
348
        return d.CloseAll, nil
×
349
}
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