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  }