...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/mocktests/harness.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/mocktests

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mocktests
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"io"
    21  	"os"
    22  	"strings"
    23  	"testing"
    24  
    25  	iamv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/iam/v1beta1"
    26  	dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
    27  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/gvks/supportedgvks"
    28  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"
    29  	corev1 "k8s.io/api/core/v1"
    30  	"k8s.io/apimachinery/pkg/api/meta"
    31  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    32  	"k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/apimachinery/pkg/runtime/schema"
    34  	yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
    35  	yamlutil "k8s.io/apimachinery/pkg/util/yaml"
    36  	"k8s.io/client-go/rest"
    37  	"sigs.k8s.io/controller-runtime/pkg/cache"
    38  	"sigs.k8s.io/controller-runtime/pkg/client"
    39  	"sigs.k8s.io/kubebuilder-declarative-pattern/mockkubeapiserver"
    40  )
    41  
    42  type Harness struct {
    43  	*testing.T
    44  
    45  	k8s        *mockkubeapiserver.MockKubeAPIServer
    46  	restConfig *rest.Config
    47  	Scheme     *runtime.Scheme
    48  	Ctx        context.Context
    49  	Client     client.Client
    50  }
    51  
    52  func (h *Harness) RESTConfig() *rest.Config {
    53  	if h.restConfig == nil {
    54  		h.Fatalf("cannot call RESTConfig before Start")
    55  	}
    56  	return h.restConfig
    57  }
    58  
    59  func (h *Harness) NewClient(cache cache.Cache, config *rest.Config, options client.Options, uncachedObjects ...client.Object) (client.Client, error) {
    60  	if h.Client == nil {
    61  		h.Fatalf("WithObjects must be called before NewClient")
    62  	}
    63  	return h.Client, nil
    64  }
    65  
    66  func NewHarness(t *testing.T) *Harness {
    67  	h := &Harness{
    68  		T:      t,
    69  		Scheme: runtime.NewScheme(),
    70  		Ctx:    context.Background(),
    71  	}
    72  	corev1.AddToScheme(h.Scheme)
    73  
    74  	iamv1beta1.SchemeBuilder.AddToScheme(h.Scheme)
    75  
    76  	t.Cleanup(h.Stop)
    77  	return h
    78  }
    79  
    80  func (h *Harness) ParseObjects(y string) []*unstructured.Unstructured {
    81  	t := h.T
    82  
    83  	var objects []*unstructured.Unstructured
    84  
    85  	decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(y)), 100)
    86  	for {
    87  		var rawObj runtime.RawExtension
    88  		if err := decoder.Decode(&rawObj); err != nil {
    89  			if err != io.EOF {
    90  				t.Fatalf("error decoding yaml: %v", err)
    91  			}
    92  			break
    93  		}
    94  
    95  		m, _, err := yamlserializer.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)
    96  		if err != nil {
    97  			t.Fatalf("error decoding yaml: %v", err)
    98  		}
    99  
   100  		unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(m)
   101  		if err != nil {
   102  			t.Fatalf("error parsing object: %v", err)
   103  		}
   104  		unstructuredObj := &unstructured.Unstructured{Object: unstructuredMap}
   105  
   106  		objects = append(objects, unstructuredObj)
   107  	}
   108  
   109  	return objects
   110  }
   111  
   112  func (h *Harness) WithObjects(initObjs ...*unstructured.Unstructured) {
   113  	k8s, err := mockkubeapiserver.NewMockKubeAPIServer(":0")
   114  	if err != nil {
   115  		h.Fatalf("error building mock kube-apiserver: %v", err)
   116  	}
   117  
   118  	k8s.RegisterType(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}, "namespaces", meta.RESTScopeRoot)
   119  	k8s.RegisterType(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Secret"}, "secrets", meta.RESTScopeNamespace)
   120  
   121  	smLoader, err := servicemappingloader.New()
   122  	if err != nil {
   123  		h.Fatalf("error getting new service mapping loader: %v", err)
   124  	}
   125  	supportedGVKs := supportedgvks.All(smLoader, dclmetadata.New())
   126  	for _, gvk := range supportedGVKs {
   127  		var resource string
   128  		switch gvk.Kind {
   129  		// TODO: Any special pluralization cases go here (unless we can get them from supportedgvks)
   130  		default:
   131  			resource = strings.ToLower(gvk.Kind) + "s"
   132  		}
   133  		k8s.RegisterType(gvk, resource, meta.RESTScopeNamespace)
   134  	}
   135  
   136  	for _, obj := range initObjs {
   137  		if err := k8s.AddObject(obj); err != nil {
   138  			h.Errorf("error adding object %v: %v", obj, err)
   139  		}
   140  	}
   141  	addr, err := k8s.StartServing()
   142  	if err != nil {
   143  		h.Errorf("error starting mock kube-apiserver: %v", err)
   144  	}
   145  
   146  	h.restConfig = &rest.Config{
   147  		Host: addr.String(),
   148  		ContentConfig: rest.ContentConfig{
   149  			ContentType: "application/json",
   150  		},
   151  	}
   152  
   153  	client, err := client.New(h.restConfig, client.Options{})
   154  	if err != nil {
   155  		h.Fatalf("error building client: %v", err)
   156  	}
   157  
   158  	h.Client = client
   159  }
   160  
   161  func (h *Harness) Stop() {
   162  	if h.k8s != nil {
   163  		if err := h.k8s.Stop(); err != nil {
   164  			h.Logf("error closing mock kube-apiserver: %v", err)
   165  			h.Errorf("error closing mock kube-apiserver: %v", err)
   166  		}
   167  	}
   168  }
   169  
   170  // MustReadFile returns the contents of the file - as a string
   171  // It fails the test if the file cannot be read
   172  func (h *Harness) MustReadFile(p string) string {
   173  	b, err := os.ReadFile(p)
   174  	if err != nil {
   175  		h.Fatalf("error reading file %q: %v", p, err)
   176  	}
   177  	return string(b)
   178  }
   179  

View as plain text