Skip to content

Commit 16ed235

Browse files
authored
Merge pull request #3 from ykulazhenkov/direct-configmap
Read configuration for the app directly from the k8s API
2 parents a838261 + ebe66b0 commit 16ed235

File tree

6 files changed

+155
-96
lines changed

6 files changed

+155
-96
lines changed

README.md

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
# network-operator-init-container
22
Init container for NVIDIA Network Operator
33

4-
The network-operator-init-container container has two required command line arguments:
4+
## Configuration
5+
The network-operator-init-container container has following required command line arguments:
56

6-
- `--config` path to the configuration file
7+
- `--configmap-name` name of the configmap with configuration for the app
8+
- `--configmap-namespace` namespace of the configmap with configuration for the app
79
- `--node-name` name of the k8s node on which this app runs
810

9-
The configuration file should be in JSON format:
11+
The ConfigMap should include configuration in JSON format:
1012

1113
```
12-
{
13-
"safeDriverLoad": {
14-
"enable": true,
15-
"annotation": "some-annotation"
16-
}
17-
}
14+
apiVersion: v1
15+
kind: ConfigMap
16+
metadata:
17+
name: ofed-init-container-config
18+
namespace: default
19+
data:
20+
config.json: |-
21+
{
22+
"safeDriverLoad": {
23+
"enable": true,
24+
"annotation": "some-annotation"
25+
}
26+
}
1827
```
1928

2029
- `safeDriverLoad` - contains settings related to safeDriverLoad feature
@@ -28,6 +37,25 @@ The container exits with code 0 when the annotation is removed from the Node obj
2837

2938
If `safeDriverLoad` feature is disabled then the container will immediately exit with code 0.
3039

40+
### Required permissions
41+
42+
```
43+
apiVersion: rbac.authorization.k8s.io/v1
44+
kind: ClusterRole
45+
metadata:
46+
name: network-operator-init-container
47+
rules:
48+
- apiGroups: [""]
49+
resources: ["nodes"]
50+
verbs: ["get", "list", "patch", "watch", "update"]
51+
- apiGroups: [""]
52+
resources: ["configmaps"]
53+
verbs: ["get", "list"]
54+
55+
```
56+
57+
## Command line arguments
58+
3159
```
3260
NVIDIA Network Operator init container
3361
@@ -36,8 +64,12 @@ Usage:
3664
3765
Config flags:
3866
39-
--config string
40-
path to the configuration file
67+
--configmap-key string
68+
key inside the configmap with configuration for the app (default "config.json")
69+
--configmap-name string
70+
name of the configmap with configuration for the app
71+
--configmap-namespace string
72+
namespace of the configmap with configuration for the app
4173
--node-name string
4274
name of the k8s node on which this app runs
4375
@@ -70,4 +102,4 @@ Kubernetes flags:
70102
--kubeconfig string
71103
Paths to a kubeconfig. Only required if out-of-cluster.
72104
73-
```
105+
```

cmd/network-operator-init-container/app/app.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,6 @@ func RunNetworkOperatorInitContainer(ctx context.Context, config *rest.Config, o
9797
"Options", opts, "Version", version.GetVersionString())
9898
ctrl.SetLogger(logger)
9999

100-
initContCfg, err := configPgk.FromFile(opts.ConfigPath)
101-
if err != nil {
102-
logger.Error(err, "failed to read configuration")
103-
return err
104-
}
105-
logger.Info("network-operator-init-container configuration", "config", initContCfg.String())
106-
107-
if !initContCfg.SafeDriverLoad.Enable {
108-
logger.Info("safe driver loading is disabled, exit")
109-
return nil
110-
}
111-
112100
mgr, err := ctrl.NewManager(config, ctrl.Options{
113101
Metrics: metricsserver.Options{BindAddress: "0"},
114102
Cache: cache.Options{
@@ -117,7 +105,7 @@ func RunNetworkOperatorInitContainer(ctx context.Context, config *rest.Config, o
117105
fmt.Sprintf("metadata.name=%s", opts.NodeName))}}},
118106
})
119107
if err != nil {
120-
logger.Error(err, "unable to start manager")
108+
logger.Error(err, "unable to create manager")
121109
return err
122110
}
123111

@@ -128,6 +116,30 @@ func RunNetworkOperatorInitContainer(ctx context.Context, config *rest.Config, o
128116
return err
129117
}
130118

119+
confConfigMap := &corev1.ConfigMap{}
120+
121+
err = k8sClient.Get(ctx, client.ObjectKey{
122+
Name: opts.ConfigMapName,
123+
Namespace: opts.ConfigMapNamespace,
124+
}, confConfigMap)
125+
126+
if err != nil {
127+
logger.Error(err, "failed to read config map with configuration")
128+
return err
129+
}
130+
131+
initContCfg, err := configPgk.Load(confConfigMap.Data[opts.ConfigMapKey])
132+
if err != nil {
133+
logger.Error(err, "failed to read configuration")
134+
return err
135+
}
136+
logger.Info("network-operator-init-container configuration", "config", initContCfg.String())
137+
138+
if !initContCfg.SafeDriverLoad.Enable {
139+
logger.Info("safe driver loading is disabled, exit")
140+
return nil
141+
}
142+
131143
errCh := make(chan error, 1)
132144

133145
if err = (&NodeReconciler{

cmd/network-operator-init-container/app/app_test.go

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
package app_test
1515

1616
import (
17+
"context"
1718
"fmt"
18-
"os"
19-
"path/filepath"
2019
"time"
2120

2221
. "github.com/onsi/ginkgo/v2"
2322
. "github.com/onsi/gomega"
23+
apiErrors "k8s.io/apimachinery/pkg/api/errors"
2424
"k8s.io/apimachinery/pkg/types"
2525
"k8s.io/apimachinery/pkg/util/json"
2626
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -34,8 +34,11 @@ import (
3434
)
3535

3636
const (
37-
testNodeName = "node1"
38-
testAnnotation = "foo.bar/spam"
37+
testConfigMapName = "test"
38+
testConfigMapNamespace = "default"
39+
testConfigMapKey = "conf"
40+
testNodeName = "node1"
41+
testAnnotation = "foo.bar/spam"
3942
)
4043

4144
func createNode(name string) *corev1.Node {
@@ -44,44 +47,67 @@ func createNode(name string) *corev1.Node {
4447
return node
4548
}
4649

47-
func createConfig(path string, cfg configPgk.Config) {
50+
func createConfig(cfg configPgk.Config) {
4851
data, err := json.Marshal(cfg)
4952
ExpectWithOffset(1, err).NotTo(HaveOccurred())
50-
ExpectWithOffset(1, os.WriteFile(path, data, 0x744))
53+
err = k8sClient.Create(ctx, &corev1.ConfigMap{
54+
ObjectMeta: metav1.ObjectMeta{Name: testConfigMapName, Namespace: testConfigMapNamespace},
55+
Data: map[string]string{testConfigMapKey: string(data)},
56+
})
57+
ExpectWithOffset(1, err).NotTo(HaveOccurred())
58+
}
59+
60+
func newOpts() *options.Options {
61+
return &options.Options{
62+
ConfigMapName: testConfigMapName,
63+
ConfigMapNamespace: testConfigMapNamespace,
64+
ConfigMapKey: testConfigMapKey,
65+
}
5166
}
5267

5368
var _ = Describe("Init container", func() {
5469
var (
55-
configPath string
70+
testCtx context.Context
71+
testCFunc context.CancelFunc
5672
)
73+
5774
BeforeEach(func() {
58-
configPath = filepath.Join(GinkgoT().TempDir(), "config")
75+
testCtx, testCFunc = context.WithCancel(ctx)
76+
})
77+
78+
AfterEach(func() {
79+
err := k8sClient.Delete(ctx, &corev1.ConfigMap{
80+
ObjectMeta: metav1.ObjectMeta{Name: testConfigMapName, Namespace: testConfigMapNamespace},
81+
})
82+
if !apiErrors.IsNotFound(err) {
83+
Expect(err).NotTo(HaveOccurred())
84+
}
85+
testCFunc()
5986
})
6087
It("Succeed", func() {
6188
testDone := make(chan interface{})
6289
go func() {
6390
defer close(testDone)
6491
defer GinkgoRecover()
65-
opts := options.New()
92+
opts := newOpts()
6693
opts.NodeName = testNodeName
67-
opts.ConfigPath = configPath
68-
createConfig(configPath, configPgk.Config{SafeDriverLoad: configPgk.SafeDriverLoadConfig{
94+
createConfig(configPgk.Config{SafeDriverLoad: configPgk.SafeDriverLoadConfig{
6995
Enable: true,
7096
Annotation: testAnnotation,
7197
}})
7298
var err error
7399
appExit := make(chan interface{})
74100
go func() {
75-
err = app.RunNetworkOperatorInitContainer(ctx, cfg, opts)
101+
err = app.RunNetworkOperatorInitContainer(testCtx, cfg, opts)
76102
close(appExit)
77103
}()
78104
node := &corev1.Node{}
79105
Eventually(func(g Gomega) {
80-
g.Expect(k8sClient.Get(ctx, types.NamespacedName{Name: testNodeName}, node)).NotTo(HaveOccurred())
106+
g.Expect(k8sClient.Get(testCtx, types.NamespacedName{Name: testNodeName}, node)).NotTo(HaveOccurred())
81107
g.Expect(node.GetAnnotations()[testAnnotation]).NotTo(BeEmpty())
82108
}, 30, 1).Should(Succeed())
83109
// remove annotation
84-
Expect(k8sClient.Patch(ctx, node, client.RawPatch(
110+
Expect(k8sClient.Patch(testCtx, node, client.RawPatch(
85111
types.MergePatchType, []byte(
86112
fmt.Sprintf(`{"metadata":{"annotations":{%q: null}}}`,
87113
testAnnotation))))).NotTo(HaveOccurred())
@@ -95,17 +121,16 @@ var _ = Describe("Init container", func() {
95121
go func() {
96122
defer close(testDone)
97123
defer GinkgoRecover()
98-
opts := options.New()
124+
opts := newOpts()
99125
opts.NodeName = "unknown-node"
100-
opts.ConfigPath = configPath
101-
createConfig(configPath, configPgk.Config{SafeDriverLoad: configPgk.SafeDriverLoadConfig{
126+
createConfig(configPgk.Config{SafeDriverLoad: configPgk.SafeDriverLoadConfig{
102127
Enable: true,
103128
Annotation: testAnnotation,
104129
}})
105130
var err error
106131
appExit := make(chan interface{})
107132
go func() {
108-
err = app.RunNetworkOperatorInitContainer(ctx, cfg, opts)
133+
err = app.RunNetworkOperatorInitContainer(testCtx, cfg, opts)
109134
close(appExit)
110135
}()
111136
Eventually(appExit, 30, 1).Should(BeClosed())
@@ -118,20 +143,19 @@ var _ = Describe("Init container", func() {
118143
go func() {
119144
defer close(testDone)
120145
defer GinkgoRecover()
121-
opts := options.New()
146+
opts := newOpts()
122147
opts.NodeName = testNodeName
123-
opts.ConfigPath = configPath
124-
createConfig(configPath, configPgk.Config{SafeDriverLoad: configPgk.SafeDriverLoadConfig{
148+
createConfig(configPgk.Config{SafeDriverLoad: configPgk.SafeDriverLoadConfig{
125149
Enable: true,
126150
Annotation: testAnnotation,
127151
}})
128152
var err error
129153
appExit := make(chan interface{})
130154
go func() {
131-
err = app.RunNetworkOperatorInitContainer(ctx, cfg, opts)
155+
err = app.RunNetworkOperatorInitContainer(testCtx, cfg, opts)
132156
close(appExit)
133157
}()
134-
cFunc()
158+
testCFunc()
135159
Eventually(appExit, 30, 1).Should(BeClosed())
136160
Expect(err).To(HaveOccurred())
137161
}()
@@ -142,13 +166,12 @@ var _ = Describe("Init container", func() {
142166
go func() {
143167
defer close(testDone)
144168
defer GinkgoRecover()
145-
opts := options.New()
169+
opts := newOpts()
146170
opts.NodeName = "unknown-node"
147-
opts.ConfigPath = configPath
148171
var err error
149172
appExit := make(chan interface{})
150173
go func() {
151-
err = app.RunNetworkOperatorInitContainer(ctx, cfg, opts)
174+
err = app.RunNetworkOperatorInitContainer(testCtx, cfg, opts)
152175
close(appExit)
153176
}()
154177
Eventually(appExit, 30, 1).Should(BeClosed())
@@ -161,16 +184,15 @@ var _ = Describe("Init container", func() {
161184
go func() {
162185
defer close(testDone)
163186
defer GinkgoRecover()
164-
opts := options.New()
187+
opts := newOpts()
165188
opts.NodeName = testNodeName
166-
opts.ConfigPath = configPath
167-
createConfig(configPath, configPgk.Config{SafeDriverLoad: configPgk.SafeDriverLoadConfig{
189+
createConfig(configPgk.Config{SafeDriverLoad: configPgk.SafeDriverLoadConfig{
168190
Enable: false,
169191
}})
170192
var err error
171193
appExit := make(chan interface{})
172194
go func() {
173-
err = app.RunNetworkOperatorInitContainer(ctx, cfg, opts)
195+
err = app.RunNetworkOperatorInitContainer(testCtx, cfg, opts)
174196
close(appExit)
175197
}()
176198
Eventually(appExit, 30, 1).Should(BeClosed())

cmd/network-operator-init-container/app/options/options.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,24 @@ func New() *Options {
3232

3333
// Options contains application options
3434
type Options struct {
35-
NodeName string
36-
ConfigPath string
37-
LogConfig *logsapi.LoggingConfiguration
35+
NodeName string
36+
ConfigMapName string
37+
ConfigMapNamespace string
38+
ConfigMapKey string
39+
LogConfig *logsapi.LoggingConfiguration
3840
}
3941

4042
// AddNamedFlagSets returns FlagSet for Options
4143
func (o *Options) AddNamedFlagSets(sharedFS *cliflag.NamedFlagSets) {
4244
configFS := sharedFS.FlagSet("Config")
4345
configFS.StringVar(&o.NodeName, "node-name", "",
4446
"name of the k8s node on which this app runs")
45-
configFS.StringVar(&o.ConfigPath, "config", "",
46-
"path to the configuration file")
47+
configFS.StringVar(&o.ConfigMapName, "configmap-name", "",
48+
"name of the configmap with configuration for the app")
49+
configFS.StringVar(&o.ConfigMapNamespace, "configmap-namespace", "",
50+
"namespace of the configmap with configuration for the app")
51+
configFS.StringVar(&o.ConfigMapKey, "configmap-key", "config.json",
52+
"key inside the configmap with configuration for the app")
4753

4854
logFS := sharedFS.FlagSet("Logging")
4955
logsapi.AddFlags(o.LogConfig, logFS)
@@ -68,8 +74,16 @@ func (o *Options) Validate() error {
6874
return fmt.Errorf("node-name is required parameter")
6975
}
7076

71-
if o.ConfigPath == "" {
72-
return fmt.Errorf("config is required parameter")
77+
if o.ConfigMapName == "" {
78+
return fmt.Errorf("configmap-name is required parameter")
79+
}
80+
81+
if o.ConfigMapNamespace == "" {
82+
return fmt.Errorf("configmap-namespace is required parameter")
83+
}
84+
85+
if o.ConfigMapKey == "" {
86+
return fmt.Errorf("configmap-key is required parameter")
7387
}
7488

7589
if err = logsapi.ValidateAndApply(o.LogConfig, nil); err != nil {

0 commit comments

Comments
 (0)