github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/config/config_test.go (about) 1 /* 2 Copyright 2018 Mirantis 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 ≈git-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 17 package config 18 19 import ( 20 "bufio" 21 "reflect" 22 "regexp" 23 "strings" 24 "testing" 25 26 "github.com/Mirantis/virtlet/tests/gm" 27 "github.com/ghodss/yaml" 28 "github.com/kballard/go-shellquote" 29 flag "github.com/spf13/pflag" 30 v1 "k8s.io/api/core/v1" 31 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 fakekube "k8s.io/client-go/kubernetes/fake" 33 34 virtlet_v1 "github.com/Mirantis/virtlet/pkg/api/virtlet.k8s/v1" 35 "github.com/Mirantis/virtlet/pkg/client/clientset/versioned/fake" 36 ) 37 38 func TestDefaultVirtletConfig(t *testing.T) { 39 gm.Verify(t, gm.NewYamlVerifier(GetDefaultConfig())) 40 } 41 42 func verifyEnv(t *testing.T, c *virtlet_v1.VirtletConfig) { 43 envText := DumpEnv(c) 44 gm.Verify(t, envText) 45 fakeEnv := map[string]string{} 46 scanner := bufio.NewScanner(strings.NewReader(envText)) 47 lineRx := regexp.MustCompile("^export ([^=]*)=(.*)") 48 for scanner.Scan() { 49 s := scanner.Text() 50 parts := lineRx.FindStringSubmatch(s) 51 if parts == nil { 52 t.Errorf("couldn't parse env string: %q", s) 53 } else { 54 subparts, err := shellquote.Split(parts[2]) 55 if err != nil { 56 t.Errorf("couldn't parse env string: %q: %v", s, err) 57 } 58 if len(subparts) != 1 { 59 t.Errorf("couldn't parse env string: %q", s) 60 } 61 fakeEnv[parts[1]] = subparts[0] 62 } 63 } 64 binder := NewBinder(nil) 65 binder.lookupEnv = func(name string) (string, bool) { 66 r, found := fakeEnv[name] 67 return r, found 68 } 69 newConf := binder.GetConfig() 70 // this is a special case (test-only flag) 71 newConf.SkipImageTranslation = c.SkipImageTranslation 72 if !reflect.DeepEqual(newConf, c) { 73 origConfYaml, err := yaml.Marshal(c) 74 if err != nil { 75 t.Fatalf("Error marshalling yaml: %v", err) 76 } 77 newConfYaml, err := yaml.Marshal(newConf) 78 if err != nil { 79 t.Fatalf("Error marshalling yaml: %v", err) 80 } 81 t.Errorf("error reloading config from env. Was:\n%s\n--- became: ---\n%s", origConfYaml, newConfYaml) 82 } 83 } 84 85 func TestMergeConfigs(t *testing.T) { 86 pstr := func(s string) *string { return &s } 87 pbool := func(b bool) *bool { return &b } 88 pint := func(i int) *int { return &i } 89 for _, tc := range []struct { 90 name, args string 91 configs []*virtlet_v1.VirtletConfig 92 }{ 93 { 94 name: "defaults", 95 args: "", 96 }, 97 { 98 name: "defaults (explicitly set as a config)", 99 args: "", 100 configs: []*virtlet_v1.VirtletConfig{GetDefaultConfig()}, 101 }, 102 { 103 name: "all cli opts", 104 args: "--fd-server-socket-path /some/fd/server.sock" + 105 " --database-path /some/file.db" + 106 " --image-download-protocol http" + 107 " --image-dir /some/image/dir" + 108 " --image-translation-configs-dir /some/translation/dir" + 109 " --libvirt-uri qemu:///foobar" + 110 " --raw-devices sd*" + 111 " --listen /some/cri.sock" + 112 " --disable-logging" + 113 " --disable-kvm" + 114 " --enable-sriov" + 115 " --cni-bin-dir /some/cni/bin/dir" + 116 " --cni-conf-dir /some/cni/conf/dir" + 117 " --calico-subnet-size 22" + 118 " --cpu-model host-model" + 119 " --enable-regexp-image-translation=false", 120 }, 121 { 122 name: "all cli opts and explicit default config", 123 args: "--fd-server-socket-path /some/fd/server.sock" + 124 " --database-path /some/file.db" + 125 " --image-download-protocol http" + 126 " --image-dir /some/image/dir" + 127 " --image-translation-configs-dir /some/translation/dir" + 128 " --libvirt-uri qemu:///foobar" + 129 " --raw-devices sd*" + 130 " --listen /some/cri.sock" + 131 " --disable-logging" + 132 " --disable-kvm" + 133 " --enable-sriov" + 134 " --cni-bin-dir /some/cni/bin/dir" + 135 " --cni-conf-dir /some/cni/conf/dir" + 136 " --calico-subnet-size 22" + 137 " --cpu-model host-model" + 138 " --enable-regexp-image-translation=false", 139 configs: []*virtlet_v1.VirtletConfig{GetDefaultConfig()}, 140 }, 141 { 142 name: "opts and configs", 143 args: "--raw-devices sd* --libvirt-uri qemu:///foobar", 144 configs: []*virtlet_v1.VirtletConfig{ 145 { 146 DisableKVM: pbool(true), 147 RawDevices: pstr("vd*"), 148 }, 149 { 150 EnableSriov: pbool(true), 151 CalicoSubnetSize: pint(22), 152 }, 153 }, 154 }, 155 } { 156 t.Run(tc.name, func(t *testing.T) { 157 flags := flag.NewFlagSet("virtlet", flag.ContinueOnError) 158 configBinder := NewBinder(flags) 159 if err := flags.Parse(strings.Split(tc.args, " ")); err != nil { 160 t.Fatalf("error parsing flags: %v", err) 161 } 162 cfg := MergeConfigs(append(tc.configs, configBinder.GetConfig())) 163 gm.Verify(t, gm.NewYamlVerifier(cfg)) 164 t.Run("env", func(t *testing.T) { 165 verifyEnv(t, cfg) 166 }) 167 }) 168 } 169 } 170 171 const ( 172 fullConfig = ` 173 config: 174 fdServerSocketPath: /some/fd/server.sock 175 databasePath: /some/file.db 176 downloadProtocol: http 177 imageTranslationConfigsDir: /some/translation/dir 178 libvirtURI: qemu:///foobar 179 rawDevices: sd* 180 criSocketPath: /some/cri.sock 181 disableLogging: true 182 disableKVM: true 183 enableSriov: true 184 cniPluginDir: /some/cni/bin/dir 185 cniConfigDir: /some/cni/conf/dir 186 calicoSubnetSize: 22 187 cpuModel: host-model 188 enableRegexpImageTranslation: false 189 logLevel: 3` 190 kubeNode1FullMapping = ` 191 spec: 192 nodeName: kube-node-1 193 priority: 10` + fullConfig 194 labelAFullMapping = ` 195 spec: 196 nodeSelector: 197 label-a: "1"` + fullConfig 198 globalFullMapping = "spec:" + fullConfig 199 anotherMapping1 = ` 200 spec: 201 nodeName: kube-node-1 202 priority: 10 203 config: 204 enableSriov: false 205 disableKVM: true` 206 anotherMapping2 = ` 207 spec: 208 nodeSelector: 209 label-b: "1" 210 priority: 1 211 config: 212 rawDevices: vd* 213 downloadProtocol: http` 214 anotherMapping3 = ` 215 spec: 216 nodeName: kube-node-2 217 priority: 10 218 config: 219 disableLogging: true` 220 anotherMapping4 = ` 221 spec: 222 nodeSelector: 223 label-a: "1" 224 config: 225 enableSriov: true 226 rawDevices: sd*` 227 sampleLocalConfig = ` 228 disableKVM: false 229 ` 230 ) 231 232 func TestConfigForNode(t *testing.T) { 233 for _, tc := range []struct { 234 name string 235 mappings []string 236 nodeName string 237 nodeLabels map[string]string 238 localConfig string 239 }{ 240 { 241 name: "no mappings", 242 }, 243 { 244 name: "mapping by node name", 245 nodeName: "kube-node-1", 246 nodeLabels: map[string]string{"label-a": "1"}, 247 mappings: []string{kubeNode1FullMapping}, 248 }, 249 { 250 name: "mapping by node labels", 251 nodeName: "kube-node-1", 252 nodeLabels: map[string]string{"label-a": "1"}, 253 mappings: []string{labelAFullMapping}, 254 }, 255 { 256 name: "mapping by node name (no match)", 257 nodeName: "kube-node-2", 258 nodeLabels: map[string]string{"label-a": "1"}, 259 mappings: []string{kubeNode1FullMapping}, 260 }, 261 { 262 name: "mapping by node labels (no match)", 263 nodeName: "kube-node-1", 264 nodeLabels: map[string]string{"label-x": "1"}, 265 mappings: []string{labelAFullMapping}, 266 }, 267 { 268 name: "global mapping", 269 nodeName: "kube-node-1", 270 nodeLabels: map[string]string{"label-a": "1"}, 271 mappings: []string{globalFullMapping}, 272 }, 273 { 274 name: "mapping by node name and multiple labels", 275 nodeName: "kube-node-1", 276 nodeLabels: map[string]string{"label-a": "1", "label-b": "1"}, 277 mappings: []string{anotherMapping1, anotherMapping2, anotherMapping3, anotherMapping4}, 278 }, 279 { 280 name: "mapping by node name and multiple labels and a local config", 281 nodeName: "kube-node-1", 282 nodeLabels: map[string]string{"label-a": "1", "label-b": "1"}, 283 mappings: []string{anotherMapping1, anotherMapping2, anotherMapping3, anotherMapping4}, 284 localConfig: sampleLocalConfig, 285 }, 286 } { 287 t.Run(tc.name, func(t *testing.T) { 288 var mappings []virtlet_v1.VirtletConfigMapping 289 for _, s := range tc.mappings { 290 var m virtlet_v1.VirtletConfigMapping 291 if err := yaml.Unmarshal([]byte(s), &m); err != nil { 292 t.Fatalf("Error parsing yaml: %v", err) 293 } 294 mappings = append(mappings, m) 295 copiedMapping := m.DeepCopyObject() 296 if !reflect.DeepEqual(copiedMapping, &m) { 297 t.Fatal("deep copy failed") 298 } 299 } 300 var localConfig *virtlet_v1.VirtletConfig 301 if tc.localConfig != "" { 302 if err := yaml.Unmarshal([]byte(tc.localConfig), &localConfig); err != nil { 303 t.Fatalf("Error parsing yaml: %v", err) 304 } 305 } 306 cfg := configForNode(mappings, localConfig, tc.nodeName, tc.nodeLabels) 307 gm.Verify(t, gm.NewYamlVerifier(cfg)) 308 }) 309 } 310 } 311 312 func TestLoadMappings(t *testing.T) { 313 pstr := func(s string) *string { return &s } 314 pbool := func(b bool) *bool { return &b } 315 nc := NewNodeConfig(nil) 316 nc.kubeClient = fakekube.NewSimpleClientset( 317 &v1.Node{ 318 ObjectMeta: meta_v1.ObjectMeta{ 319 Name: "kube-node-1", 320 Labels: map[string]string{ 321 "label-a": "1", 322 "label-b": "1", 323 }, 324 }, 325 }) 326 nc.virtletClient = fake.NewSimpleClientset( 327 &virtlet_v1.VirtletConfigMapping{ 328 ObjectMeta: meta_v1.ObjectMeta{ 329 Name: "mapping-1", 330 Namespace: "kube-system", 331 }, 332 Spec: virtlet_v1.VirtletConfigMappingSpec{ 333 NodeName: "kube-node-1", 334 Config: &virtlet_v1.VirtletConfig{ 335 EnableSriov: pbool(true), 336 DisableKVM: pbool(true), 337 }, 338 }, 339 }, 340 &virtlet_v1.VirtletConfigMapping{ 341 ObjectMeta: meta_v1.ObjectMeta{ 342 Name: "mapping-2", 343 Namespace: "kube-system", 344 }, 345 Spec: virtlet_v1.VirtletConfigMappingSpec{ 346 NodeSelector: map[string]string{"label-a": "1"}, 347 Config: &virtlet_v1.VirtletConfig{ 348 RawDevices: pstr("sd*"), 349 }, 350 }, 351 }) 352 cfg, err := nc.LoadConfig(&virtlet_v1.VirtletConfig{ 353 EnableSriov: pbool(false), 354 }, "kube-node-1") 355 if err != nil { 356 t.Fatalf("LoadConfig(): %v", err) 357 } 358 gm.Verify(t, gm.NewYamlVerifier(cfg)) 359 } 360 361 func TestGenerateDoc(t *testing.T) { 362 gm.Verify(t, GenerateDoc()) 363 }