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

k8snetworkplumbingwg / sriov-network-operator / 10179580977

31 Jul 2024 11:45AM UTC coverage: 43.816% (-0.2%) from 43.968%
10179580977

Pull #733

github

web-flow
Merge d80add92c into 57e1e9056
Pull Request #733: feat: implement MlxResetFW to reset the FW on VF changes

1 of 63 new or added lines in 6 files covered. (1.59%)

6 existing lines in 2 files now uncovered.

6536 of 14917 relevant lines covered (43.82%)

0.48 hits per line

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

5.43
/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
        "strings"
25
        "time"
26

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

38
        configv1 "github.com/openshift/api/config/v1"
39
        mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
40

41
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
42
        snclientset "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/client/clientset/versioned"
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/platforms"
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
        }
91
)
92

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

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

×
108
        // Mark that we are running inside a container
×
109
        vars.UsingSystemdMode = false
×
110
        if startOpts.systemd {
×
111
                vars.UsingSystemdMode = true
×
112
        }
×
113

114
        vars.ParallelNicConfig = startOpts.parallelNicConfig
×
115
        vars.ManageSoftwareBridges = startOpts.manageSoftwareBridges
×
116

×
117
        if startOpts.nodeName == "" {
×
118
                name, ok := os.LookupEnv("NODE_NAME")
×
119
                if !ok || name == "" {
×
120
                        return fmt.Errorf("node-name is required")
×
121
                }
×
122
                startOpts.nodeName = name
×
123
        }
124
        vars.NodeName = startOpts.nodeName
×
125

×
126
        for _, p := range startOpts.disabledPlugins {
×
127
                if _, ok := vars.DisableablePlugins[p]; !ok {
×
128
                        return fmt.Errorf("%s plugin cannot be disabled", p)
×
129
                }
×
130
        }
131

132
        // This channel is used to ensure all spawned goroutines exit when we exit.
133
        stopCh := make(chan struct{})
×
134
        defer close(stopCh)
×
135

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

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

×
147
        refreshCh := make(chan daemon.Message)
×
148
        defer close(refreshCh)
×
149

×
150
        var config *rest.Config
×
151
        var err error
×
152

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

×
163
                urlPath, err := url.Parse(apiURL)
×
164
                if err != nil {
×
165
                        setupLog.Error(err, "failed to parse api url from kubelet kubeconfig")
×
166
                }
×
167

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

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

189
        if err != nil {
×
190
                return err
×
191
        }
×
192

193
        vars.Config = config
×
194
        vars.Scheme = scheme.Scheme
×
195

×
196
        closeAllConns, err := updateDialer(config)
×
197
        if err != nil {
×
198
                return err
×
199
        }
×
200

201
        err = sriovnetworkv1.AddToScheme(scheme.Scheme)
×
202
        if err != nil {
×
203
                setupLog.Error(err, "failed to load sriov network CRDs to scheme")
×
204
                return err
×
205
        }
×
206

207
        err = mcfgv1.AddToScheme(scheme.Scheme)
×
208
        if err != nil {
×
209
                setupLog.Error(err, "failed to load machine config CRDs to scheme")
×
210
                return err
×
211
        }
×
212

213
        err = configv1.Install(scheme.Scheme)
×
214
        if err != nil {
×
215
                setupLog.Error(err, "failed to load openshift config CRDs to scheme")
×
216
                return err
×
217
        }
×
218

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

225
        snclient := snclientset.NewForConfigOrDie(config)
×
226
        kubeclient := kubernetes.NewForConfigOrDie(config)
×
227

×
228
        hostHelpers, err := helper.NewDefaultHostHelpers()
×
229
        if err != nil {
×
230
                setupLog.Error(err, "failed to create hostHelpers")
×
231
                return err
×
232
        }
×
233

234
        platformHelper, err := platforms.NewDefaultPlatformHelper()
×
235
        if err != nil {
×
236
                setupLog.Error(err, "failed to create platformHelper")
×
237
                return err
×
238
        }
×
239

240
        config.Timeout = 5 * time.Second
×
241
        writerclient := snclientset.NewForConfigOrDie(config)
×
242

×
243
        eventRecorder := daemon.NewEventRecorder(writerclient, kubeclient)
×
244
        defer eventRecorder.Shutdown()
×
245

×
246
        setupLog.V(0).Info("starting node writer")
×
247
        nodeWriter := daemon.NewNodeStateStatusWriter(writerclient,
×
248
                closeAllConns,
×
249
                eventRecorder,
×
250
                hostHelpers,
×
251
                platformHelper)
×
252

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

×
266
        if err := sriovnetworkv1.InitNicIDMapFromConfigMap(kubeclient, vars.Namespace); err != nil {
×
267
                setupLog.Error(err, "failed to run init NicIdMap")
×
268
                return err
×
269
        }
×
270

271
        eventRecorder.SendEvent("ConfigDaemonStart", "Config Daemon starting")
×
272

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

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

×
293
        setupLog.V(0).Info("Starting SriovNetworkConfigDaemon")
×
294
        err = daemon.New(
×
295
                kClient,
×
296
                snclient,
×
297
                kubeclient,
×
298
                hostHelpers,
×
299
                platformHelper,
×
300
                exitCh,
×
301
                stopCh,
×
302
                syncCh,
×
303
                refreshCh,
×
304
                eventRecorder,
×
NEW
305
                featureGates,
×
306
                startOpts.disabledPlugins,
×
307
        ).Run(stopCh, exitCh)
×
308
        if err != nil {
×
309
                setupLog.Error(err, "failed to run daemon")
×
310
        }
×
311
        setupLog.V(0).Info("Shutting down SriovNetworkConfigDaemon")
×
312
        return err
×
313
}
314

315
// updateDialer instruments a restconfig with a dial. the returned function allows forcefully closing all active connections.
316
func updateDialer(clientConfig *rest.Config) (func(), error) {
×
317
        if clientConfig.Transport != nil || clientConfig.Dial != nil {
×
318
                return nil, fmt.Errorf("there is already a transport or dialer configured")
×
319
        }
×
320
        f := &net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}
×
321
        d := connrotation.NewDialer(f.DialContext)
×
322
        clientConfig.Dial = d.DialContext
×
323
        return d.CloseAll, nil
×
324
}
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