1 package cniplugin
2
3 import (
4 "bytes"
5 "context"
6 _ "embed"
7 "encoding/json"
8 "os"
9 "testing"
10 "text/template"
11
12 v1 "k8s.io/api/core/v1"
13
14 "github.com/spf13/afero"
15 "github.com/stretchr/testify/assert"
16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17 "sigs.k8s.io/controller-runtime/pkg/client/fake"
18
19 kruntime "k8s.io/apimachinery/pkg/runtime"
20 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
21 clientgoscheme "k8s.io/client-go/kubernetes/scheme"
22
23 "edge-infra.dev/pkg/edge/constants"
24 "edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
25 "edge-infra.dev/test/f2"
26 )
27
28 var (
29
30 expectedScript string
31 path = "/opt/cni/bin/gateway"
32 cm = v1.ConfigMap{
33 ObjectMeta: metav1.ObjectMeta{
34 Name: "network-service-ips",
35 Namespace: constants.SdsNamespace,
36 },
37 Data: constants.ClusterNetworkServiceDefaults,
38 }
39 emptyCM = v1.ConfigMap{
40 ObjectMeta: metav1.ObjectMeta{
41 Name: "network-service-ips",
42 Namespace: constants.SdsNamespace,
43 },
44 Data: map[string]string{
45 constants.ServiceTypePodNetworkCIDR: "",
46 constants.ServiceTypeClusterDNS: "",
47 constants.ServiceTypeServiceNetworkCIDR: "",
48 },
49 }
50 )
51
52 var f f2.Framework
53
54 func TestMain(m *testing.M) {
55 f = f2.New(context.Background(), f2.WithExtensions()).
56 Setup().
57 Teardown()
58 os.Exit(f.Run(m))
59 }
60
61 func TestAddCNIScript(t *testing.T) {
62 feature := f2.NewFeature("CNI scripts").
63 Test("plugin reconciles", func(ctx f2.Context, t *testing.T) f2.Context {
64 memFS, err := createMemFS(false)
65 assert.NoError(t, err)
66
67 kclient := fake.NewClientBuilder().WithScheme(createScheme()).WithObjects(&cm).Build()
68
69 cfg := config.NewConfig(kclient, nil, nil, config.Flags{}).WithFs(memFS)
70 _, err = CNIPlugin{}.Reconcile(context.Background(), nil, cfg)
71 assert.NoError(t, err)
72
73 assertValidCNIConfig(t, memFS)
74 return ctx
75 }).
76 Test("plugin reconciles with defaults pod and service cidr", func(ctx f2.Context, t *testing.T) f2.Context {
77 memFS, err := createMemFS(false)
78 assert.NoError(t, err)
79
80 kclient := fake.NewClientBuilder().WithScheme(createScheme()).Build()
81
82 cfg := config.NewConfig(kclient, nil, nil, config.Flags{}).WithFs(memFS)
83 _, err = CNIPlugin{}.Reconcile(context.Background(), nil, cfg)
84 assert.NoError(t, err)
85
86 assertValidCNIConfig(t, memFS)
87 return ctx
88 }).
89 Test("plugin reconciles with empty pod and service cidrs", func(ctx f2.Context, t *testing.T) f2.Context {
90 memFS, err := createMemFS(false)
91 assert.NoError(t, err)
92
93 kclient := fake.NewClientBuilder().WithScheme(createScheme()).WithObjects(&emptyCM).Build()
94
95 cfg := config.NewConfig(kclient, nil, nil, config.Flags{}).WithFs(memFS)
96 _, err = CNIPlugin{}.Reconcile(context.Background(), nil, cfg)
97 assert.NoError(t, err)
98
99 assertValidCNIConfig(t, memFS)
100 return ctx
101 }).Feature()
102
103 f.Test(t, feature)
104 }
105
106 func assertValidCNIConfig(t *testing.T, memFS afero.Fs) {
107 contents, err := afero.ReadFile(memFS, path)
108 assert.NoError(t, err)
109 expected, err := renderExpectedScript()
110 assert.NoError(t, err)
111 assert.Equal(t, expected, string(contents))
112
113 calicoConf, err := afero.ReadFile(memFS, calicoConfListPath)
114 assert.NoError(t, err)
115 assert.Equal(t, mockCalicoConfig(true), calicoConf)
116
117 fileInfo, err := memFS.Stat(path)
118 assert.NoError(t, err)
119 assert.Equal(t, pluginFSMode, fileInfo.Mode())
120
121 fileInfo, err = memFS.Stat(calicoConfListPath)
122 assert.NoError(t, err)
123 assert.Equal(t, calicoConfFSMode, fileInfo.Mode())
124 }
125
126 func createScheme() *kruntime.Scheme {
127 scheme := kruntime.NewScheme()
128 utilruntime.Must(clientgoscheme.AddToScheme(scheme))
129 utilruntime.Must(v1.AddToScheme(scheme))
130 return scheme
131 }
132
133 func mockCalicoConfig(pluginExists bool) []byte {
134 conf := CalicoConfList{
135 Plugins: []Plugin{
136 {
137 Type: "test",
138 },
139 },
140 }
141
142 if pluginExists {
143 conf.Plugins = append(conf.Plugins, Plugin{Type: "gateway", Capabilities: &Capabilities{EnableAnnotation: true}})
144 }
145
146 b, _ := json.MarshalIndent(conf, "", " ")
147 return b
148 }
149
150 func createMemFS(pluginExists bool) (afero.Fs, error) {
151 contents := mockCalicoConfig(pluginExists)
152 fs := afero.NewMemMapFs()
153 if err := afero.WriteFile(fs, calicoConfListPath, contents, 0644); err != nil {
154 return nil, err
155 }
156 return fs, nil
157 }
158
159 func renderExpectedScript() (string, error) {
160 tmpl, err := template.New("test").Parse(expectedScript)
161 if err != nil {
162 return "", err
163 }
164 var tpl bytes.Buffer
165 values := templateVariables{
166 ServiceNetworkCidr: constants.ClusterNetworkServiceDefaults[constants.ServiceTypeServiceNetworkCIDR],
167 PodNetworkCidr: constants.ClusterNetworkServiceDefaults[constants.ServiceTypePodNetworkCIDR],
168 }
169 if err = tmpl.Execute(&tpl, values); err != nil {
170 return "", err
171 }
172 return tpl.String(), nil
173 }
174
View as plain text