github.com/webmeshproj/webmesh-cni@v0.0.27/internal/config/provider_test.go (about) 1 /* 2 Copyright 2023 Avi Zimmerman <avi.zimmerman@gmail.com>. 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 17 package config 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "strings" 24 "testing" 25 "time" 26 27 kjson "github.com/knadh/koanf/parsers/json" 28 "github.com/knadh/koanf/v2" 29 corev1 "k8s.io/api/core/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/runtime" 32 clientgoscheme "k8s.io/client-go/kubernetes/scheme" 33 "k8s.io/client-go/rest" 34 ctrl "sigs.k8s.io/controller-runtime" 35 "sigs.k8s.io/controller-runtime/pkg/client" 36 "sigs.k8s.io/controller-runtime/pkg/envtest" 37 "sigs.k8s.io/controller-runtime/pkg/log/zap" 38 ) 39 40 func TestConfigMapLoader(t *testing.T) { 41 ctx := context.Background() 42 cfg, cli := setupProviderTest(t) 43 provider := NewConfigMapProvider(cfg, client.ObjectKey{ 44 Name: "config", 45 Namespace: "default", 46 }) 47 // Create a configmap that tests the various types we'll encounter 48 err := cli.Create(ctx, &corev1.ConfigMap{ 49 ObjectMeta: metav1.ObjectMeta{ 50 Name: "config", 51 Namespace: "default", 52 }, 53 Data: map[string]string{ 54 "manager.metrics-address": "localhost:8080", 55 "manager.reconcile-timeout": "1s", 56 "manager.max-concurrent-reconciles": "10", 57 "manager.cluster-dns-selector": `{"k8s-app": "kube-dns"}`, 58 "manager.enable-metadata-server": "false", 59 "storage.cache-sync-timeout": "2s", 60 "host.wireguard.listen-port": "51820", 61 }, 62 }) 63 if err != nil { 64 t.Fatal("Failed to create configmap", err) 65 } 66 k := koanf.New(".") 67 err = k.Load(provider, kjson.Parser()) 68 if err != nil { 69 t.Fatal("Failed to load configmap", err) 70 } 71 var c Config 72 err = k.Unmarshal("", &c) 73 if err != nil { 74 t.Fatal("Failed to unmarshal configmap", err) 75 } 76 if c.Manager.MetricsAddress != "localhost:8080" { 77 t.Fatalf("Expected manager.metrics-address to be localhost:8080, got %s", c.Manager.MetricsAddress) 78 } 79 if c.Manager.ReconcileTimeout != time.Second { 80 t.Fatalf("Expected manager.reconcile-timeout to be 1s, got %s", c.Manager.ReconcileTimeout) 81 } 82 if c.Manager.MaxConcurrentReconciles != 10 { 83 t.Fatalf("Expected manager.max-concurrent-reconciles to be 10, got %d", c.Manager.MaxConcurrentReconciles) 84 } 85 if c.Manager.ClusterDNSSelector["k8s-app"] != "kube-dns" { 86 t.Fatalf("Expected manager.cluster-dns-selector to be {\"k8s-app\": \"kube-dns\"}, got %s", c.Manager.ClusterDNSSelector) 87 } 88 if c.Manager.EnableMetadataServer { 89 t.Fatal("Expected manager.enable-metadata-server to be false, got true") 90 } 91 if c.Storage.CacheSyncTimeout != 2*time.Second { 92 t.Fatalf("Expected storage.cache-sync-timeout to be 2s, got %s", c.Storage.CacheSyncTimeout) 93 } 94 if c.Host.WireGuard.ListenPort != 51820 { 95 t.Fatalf("Expected host.wireguard.listen-port to be 51820, got %d", c.Host.WireGuard.ListenPort) 96 } 97 } 98 99 func TestConfigMapProvider(t *testing.T) { 100 ctx := context.Background() 101 cfg, cli := setupProviderTest(t) 102 103 tc := []struct { 104 name string 105 cmdata map[string]string 106 expected map[string]any 107 }{ 108 { 109 name: "SingleValue", 110 cmdata: map[string]string{ 111 "key": "value", 112 }, 113 expected: map[string]any{ 114 "key": "value", 115 }, 116 }, 117 { 118 name: "NestedValues", 119 cmdata: map[string]string{ 120 "key": "value", 121 "nested.key": "nested.value", 122 }, 123 expected: map[string]any{ 124 "key": "value", 125 "nested": map[string]any{ 126 "key": "nested.value", 127 }, 128 }, 129 }, 130 { 131 name: "TripleNestedValues", 132 cmdata: map[string]string{ 133 "key": "value", 134 "nested.foo.bar": "baz", 135 }, 136 expected: map[string]any{ 137 "key": "value", 138 "nested": map[string]any{ 139 "foo": map[string]any{ 140 "bar": "baz", 141 }, 142 }, 143 }, 144 }, 145 { 146 name: "SingleValueSupportedTypes", 147 cmdata: map[string]string{ 148 "string": "value", 149 "number": "0", 150 "bool": "true", 151 "duration": "1s", 152 "slice": "[1, 2, 3]", 153 }, 154 expected: map[string]any{ 155 "string": "value", 156 "number": 0, 157 "bool": true, 158 "duration": time.Second, 159 "slice": []int{1, 2, 3}, 160 }, 161 }, 162 } 163 164 for _, tt := range tc { 165 t.Run(tt.name, func(t *testing.T) { 166 err := cli.Create(ctx, &corev1.ConfigMap{ 167 ObjectMeta: metav1.ObjectMeta{ 168 Name: strings.ToLower(tt.name), 169 Namespace: "default", 170 }, 171 Data: tt.cmdata, 172 }) 173 if err != nil { 174 t.Fatal("Failed to create configmap", err) 175 } 176 p := NewConfigMapProvider(cfg, client.ObjectKey{ 177 Name: strings.ToLower(tt.name), 178 Namespace: "default", 179 }) 180 data, err := p.Read() 181 if err != nil { 182 t.Fatal("Failed to read configmap", err) 183 } 184 if len(data) != len(tt.expected) { 185 t.Fatalf("Expected %d keys, got %d", len(tt.expected), len(data)) 186 } 187 dataJSON, err := p.ReadBytes() 188 if err != nil { 189 t.Fatal("Failed to read configmap", err) 190 } 191 expectedJSON, _ := json.Marshal(tt.expected) 192 if !bytes.Equal(expectedJSON, dataJSON) { 193 t.Fatalf("Expected %s, got %s", string(expectedJSON), string(dataJSON)) 194 } 195 }) 196 } 197 } 198 199 func setupProviderTest(t *testing.T) (*rest.Config, client.Client) { 200 t.Helper() 201 t.Log("Starting test environment") 202 ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) 203 testenv := envtest.Environment{ 204 ControlPlaneStartTimeout: time.Second * 20, 205 ControlPlaneStopTimeout: time.Second * 10, 206 } 207 cfg, err := testenv.Start() 208 if err != nil { 209 t.Fatal("Failed to start test environment", err) 210 } 211 t.Cleanup(func() { 212 t.Log("Stopping test environment") 213 err := testenv.Stop() 214 if err != nil { 215 t.Log("Failed to stop test environment", err) 216 } 217 }) 218 scheme := runtime.NewScheme() 219 err = clientgoscheme.AddToScheme(scheme) 220 if err != nil { 221 t.Fatal("Failed to add client-go scheme to runtime scheme", err) 222 } 223 cli, err := client.New(cfg, client.Options{ 224 Scheme: scheme, 225 Cache: &client.CacheOptions{ 226 DisableFor: []client.Object{&corev1.ConfigMap{}}, 227 }, 228 }) 229 if err != nil { 230 t.Fatal("Failed to create client", err) 231 } 232 return cfg, cli 233 }