1
2
3
4
5
6
7
8
9
10
11
12
13
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
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
171
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