Skip to content

Commit 4363073

Browse files
committed
chore: modernize unmanageddetector test
We can use envtest and avoid a lot of the setup complexity.
1 parent 3132f8d commit 4363073

File tree

4 files changed

+134
-62
lines changed

4 files changed

+134
-62
lines changed

pkg/controller/lifecyclehandler/handler_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ func TestIsOrphaned(t *testing.T) {
150150
h := test.NewKubeHarness(ctx, t)
151151
c := h.GetClient()
152152

153-
h.CreateDummyCRD("test1.cnrm.cloud.google.com", "v1alpha1", "Test1Foo")
154-
h.CreateDummyCRD("test1.cnrm.cloud.google.com", "v1alpha1", "Test1Bar")
153+
h.CreateDummyCRD(schema.GroupVersionKind{Group: "test1.cnrm.cloud.google.com", Version: "v1alpha1", Kind: "Test1Foo"})
154+
h.CreateDummyCRD(schema.GroupVersionKind{Group: "test1.cnrm.cloud.google.com", Version: "v1alpha1", Kind: "Test1Bar"})
155155

156156
for _, tc := range tests {
157157
tc := tc

pkg/controller/unmanageddetector/controller_test.go

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,30 +26,69 @@ import (
2626
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/randomid"
2727
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
2828
testcontroller "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/controller"
29-
testmain "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/main"
3029
testvariable "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/resourcefixture/variable"
3130
"github.com/google/go-cmp/cmp"
3231

3332
corev1 "k8s.io/api/core/v1"
34-
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
35-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3633
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34+
"k8s.io/apimachinery/pkg/runtime/schema"
3735
"k8s.io/apimachinery/pkg/types"
36+
"k8s.io/client-go/rest"
3837
"sigs.k8s.io/controller-runtime/pkg/client"
3938
"sigs.k8s.io/controller-runtime/pkg/manager"
4039
"sigs.k8s.io/controller-runtime/pkg/reconcile"
4140
)
4241

4342
var (
44-
mgr manager.Manager
45-
46-
fakeCRD = newTestKindCRD()
43+
fakeCRDGVK = schema.GroupVersionKind{
44+
Group: "test.cnrm.cloud.google.com",
45+
Version: "v1beta1",
46+
Kind: "TestKind",
47+
}
4748
)
4849

50+
func runManager(t *testing.T, restConfig *rest.Config) manager.Manager {
51+
mgrOpts := manager.Options{}
52+
mgrOpts.Metrics.BindAddress = "0" // no metrics
53+
54+
mgr, err := manager.New(restConfig, mgrOpts)
55+
if err != nil {
56+
t.Fatalf("error creating manager: %v", err)
57+
}
58+
59+
ctx := context.TODO()
60+
ctx, cancel := context.WithCancel(ctx)
61+
62+
errChan := make(chan error)
63+
go func() {
64+
err := mgr.Start(ctx)
65+
if err != nil {
66+
t.Errorf("error from manager: %v", err)
67+
}
68+
errChan <- err
69+
}()
70+
71+
t.Cleanup(func() {
72+
cancel()
73+
err := <-errChan
74+
if err != nil {
75+
t.Fatalf("error from manager: %v", err)
76+
}
77+
})
78+
79+
return mgr
80+
}
81+
4982
func TestReconcile_UnmanagedResource(t *testing.T) {
50-
t.Parallel()
83+
ctx := context.TODO()
84+
h := test.NewKubeHarness(ctx, t)
85+
client := h.GetClient()
86+
87+
h.CreateDummyCRD(fakeCRDGVK)
88+
89+
mgr := runManager(t, h.GetRESTConfig())
90+
5191
testID := testvariable.NewUniqueID()
52-
client := mgr.GetClient()
5392
testcontroller.EnsureNamespaceExistsT(t, client, k8s.SystemNamespace)
5493
testcontroller.EnsureNamespaceExistsT(t, client, testID)
5594

@@ -60,22 +99,22 @@ func TestReconcile_UnmanagedResource(t *testing.T) {
6099
resource := newTestKindUnstructured(resourceNN)
61100
test.EnsureObjectExists(t, resource, client)
62101

63-
reconciler, err := unmanageddetector.NewReconciler(mgr, fakeCRD)
102+
reconciler, err := unmanageddetector.NewReconciler(mgr, fakeCRDGVK)
64103
if err != nil {
65-
t.Fatal(fmt.Errorf("error creating reconciler: %w", err))
104+
t.Fatalf("error creating reconciler: %v", err)
66105
}
67-
res, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: resourceNN})
106+
res, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: resourceNN})
68107
if err != nil {
69-
t.Fatal(fmt.Errorf("unexpected error during reconciliation: %w", err))
108+
t.Fatalf("unexpected error during reconciliation: %v", err)
70109
}
71110
emptyResult := reconcile.Result{}
72111
if got, want := res, emptyResult; !reflect.DeepEqual(got, want) {
73112
t.Fatalf("unexpected diff in reconcile result (-want +got): \n%v", cmp.Diff(want, got))
74113
}
75114

76-
condition, found, err := getCurrentCondition(context.TODO(), client, resource)
115+
condition, found, err := getCurrentCondition(ctx, client, resource)
77116
if err != nil {
78-
t.Fatal(fmt.Errorf("error getting resource's condition: %w", err))
117+
t.Fatalf("error getting resource's condition: %v", err)
79118
}
80119
if !found {
81120
t.Fatalf("got nil condition for resource, want non-nil condition with reason '%v'", k8s.Unmanaged)
@@ -89,9 +128,15 @@ func TestReconcile_UnmanagedResource(t *testing.T) {
89128
}
90129

91130
func TestReconcile_ManagedResource(t *testing.T) {
92-
t.Parallel()
131+
ctx := context.TODO()
132+
h := test.NewKubeHarness(ctx, t)
133+
client := h.GetClient()
134+
135+
h.CreateDummyCRD(fakeCRDGVK)
136+
137+
mgr := runManager(t, h.GetRESTConfig())
138+
93139
testID := testvariable.NewUniqueID()
94-
client := mgr.GetClient()
95140
testcontroller.EnsureNamespaceExistsT(t, client, k8s.SystemNamespace)
96141
testcontroller.EnsureNamespaceExistsT(t, client, testID)
97142

@@ -105,48 +150,33 @@ func TestReconcile_ManagedResource(t *testing.T) {
105150
controller := newControllerUnstructuredForNamespace(resourceNN.Namespace)
106151
test.EnsureObjectExists(t, controller, client)
107152

108-
reconciler, err := unmanageddetector.NewReconciler(mgr, fakeCRD)
153+
reconciler, err := unmanageddetector.NewReconciler(mgr, fakeCRDGVK)
109154
if err != nil {
110-
t.Fatal(fmt.Errorf("error creating reconciler: %w", err))
155+
t.Fatalf("error creating reconciler: %v", err)
111156
}
112-
res, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: resourceNN})
157+
res, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: resourceNN})
113158
if err != nil {
114-
t.Fatal(fmt.Errorf("unexpected error during reconciliation: %w", err))
159+
t.Fatalf("unexpected error during reconciliation: %v", err)
115160
}
116161
emptyResult := reconcile.Result{}
117162
if got, want := res, emptyResult; !reflect.DeepEqual(got, want) {
118163
t.Fatalf("unexpected diff in reconcile result (-want +got): \n%v", cmp.Diff(want, got))
119164
}
120165

121-
condition, found, err := getCurrentCondition(context.TODO(), client, resource)
166+
condition, found, err := getCurrentCondition(ctx, client, resource)
122167
if err != nil {
123-
t.Fatal(fmt.Errorf("error getting resource's condition: %w", err))
168+
t.Fatalf("error getting resource's condition: %v", err)
124169
}
125170
if found {
126171
t.Fatalf("got non-nil condition '%v' for resource, want nil condition", condition)
127172
}
128173
}
129174

130-
func newTestKindCRD() *apiextensions.CustomResourceDefinition {
131-
crd := test.CRDForGVK(metav1.GroupVersionKind{
132-
Group: "test.cnrm.cloud.google.com",
133-
Version: "v1beta1",
134-
Kind: "TestKind",
135-
})
136-
// Enable the status subresource for this CRD. This is needed to allow
137-
// UpdateStatus() calls to work on custom resources belonging to this CRD
138-
// on the API server.
139-
crd.Spec.Versions[0].Subresources = &apiextensions.CustomResourceSubresources{
140-
Status: &apiextensions.CustomResourceSubresourceStatus{},
141-
}
142-
return crd
143-
}
144-
145175
func newTestKindUnstructured(nn types.NamespacedName) *unstructured.Unstructured {
146176
return &unstructured.Unstructured{
147177
Object: map[string]interface{}{
148-
"apiVersion": fmt.Sprintf("%v/%v", fakeCRD.Spec.Group, k8s.GetVersionFromCRD(fakeCRD)),
149-
"kind": fakeCRD.Spec.Names.Kind,
178+
"apiVersion": fmt.Sprintf("%v/%v", fakeCRDGVK.Group, fakeCRDGVK.Version),
179+
"kind": fakeCRDGVK.Kind,
150180
"metadata": map[string]interface{}{
151181
"namespace": nn.Namespace,
152182
"name": nn.Name,
@@ -204,7 +234,3 @@ func getCurrentCondition(ctx context.Context, c client.Client, u *unstructured.U
204234
condition, found = k8s.GetReadyCondition(resource)
205235
return condition, found, nil
206236
}
207-
208-
func TestMain(m *testing.M) {
209-
testmain.ForUnitTestsWithCRDs(m, []*apiextensions.CustomResourceDefinition{fakeCRD}, &mgr)
210-
}

pkg/test/kubeharness.go

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3030
apierrors "k8s.io/apimachinery/pkg/api/errors"
3131
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32+
"k8s.io/apimachinery/pkg/runtime/schema"
3233
"k8s.io/apimachinery/pkg/types"
3334
"k8s.io/apimachinery/pkg/util/wait"
3435
"k8s.io/client-go/rest"
@@ -235,21 +236,22 @@ func (h *KubeHarness) EnsureNamespaceExists(name string) {
235236
}
236237

237238
// CreateDummyCRD registers a CRD so we can create objects in tests
238-
func (h *KubeHarness) CreateDummyCRD(group, version, kind string) {
239+
func (h *KubeHarness) CreateDummyCRD(gvk schema.GroupVersionKind) {
239240
ctx := h.Ctx
240241

241-
resource := strings.ToLower(kind) + "s"
242+
resource := strings.ToLower(gvk.Kind) + "s" // It's only a test
243+
242244
crd := &apiextensions.CustomResourceDefinition{}
243245
crd.SetGroupVersionKind(apiextensions.SchemeGroupVersion.WithKind("CustomResourceDefinition"))
244246

245-
crd.SetName(resource + "." + group)
246-
crd.Spec.Group = group
247-
crd.Spec.Names.Kind = kind
247+
crd.SetName(resource + "." + gvk.Group)
248+
crd.Spec.Group = gvk.Group
249+
crd.Spec.Names.Kind = gvk.Kind
248250
crd.Spec.Names.Plural = resource
249251
crd.Spec.Scope = apiextensions.NamespaceScoped
250252

251253
crd.Spec.Versions = append(crd.Spec.Versions, apiextensions.CustomResourceDefinitionVersion{
252-
Name: version,
254+
Name: gvk.Version,
253255
Served: true,
254256
Storage: true,
255257
Schema: &apiextensions.CustomResourceValidation{
@@ -259,11 +261,54 @@ func (h *KubeHarness) CreateDummyCRD(group, version, kind string) {
259261
"spec": {
260262
Type: "object",
261263
},
264+
"status": {
265+
Type: "object",
266+
267+
Properties: map[string]apiextensions.JSONSchemaProps{
268+
"observedGeneration": {
269+
Type: "integer",
270+
Format: "int64",
271+
},
272+
"conditions": {
273+
Type: "array",
274+
Items: &apiextensions.JSONSchemaPropsOrArray{
275+
Schema: &apiextensions.JSONSchemaProps{
276+
Type: "object",
277+
Properties: map[string]apiextensions.JSONSchemaProps{
278+
"type": {
279+
Type: "string",
280+
},
281+
"status": {
282+
Type: "string",
283+
},
284+
"lastTransitionTime": {
285+
Type: "string",
286+
Format: "date-time",
287+
},
288+
"reason": {
289+
Type: "string",
290+
},
291+
"message": {
292+
Type: "string",
293+
},
294+
},
295+
},
296+
},
297+
},
298+
},
299+
},
262300
},
263301
},
264302
},
265303
})
266304

305+
// Enable the status subresource for this CRD. This is needed to allow
306+
// UpdateStatus() calls to work on custom resources belonging to this CRD
307+
// on the API server.
308+
crd.Spec.Versions[0].Subresources = &apiextensions.CustomResourceSubresources{
309+
Status: &apiextensions.CustomResourceSubresourceStatus{},
310+
}
311+
267312
if err := h.client.Create(ctx, crd); err != nil {
268313
h.Fatalf("error creating crd %v: %v", crd.GroupVersionKind(), err)
269314
}

pkg/test/resources.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,50 +29,51 @@ import (
2929
"k8s.io/apimachinery/pkg/api/errors"
3030
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3131
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32+
"k8s.io/apimachinery/pkg/runtime/schema"
3233
"sigs.k8s.io/controller-runtime/pkg/client"
3334
)
3435

3536
const Namespace = "namespace-1"
3637

3738
func FakeCRDs() []*apiextensions.CustomResourceDefinition {
3839
return []*apiextensions.CustomResourceDefinition{
39-
CRDForGVK(metav1.GroupVersionKind{
40+
CRDForGVK(schema.GroupVersionKind{
4041
Group: "test1.cnrm.cloud.google.com",
4142
Version: "v1alpha1",
4243
Kind: "Test1Foo",
4344
}),
44-
CRDForGVK(metav1.GroupVersionKind{
45+
CRDForGVK(schema.GroupVersionKind{
4546
Group: "test1.cnrm.cloud.google.com",
4647
Version: "v1alpha1",
4748
Kind: "Test1Bar",
4849
}),
49-
CRDForGVK(metav1.GroupVersionKind{
50+
CRDForGVK(schema.GroupVersionKind{
5051
// Unique group
5152
Group: "test2.cnrm.cloud.google.com",
5253
Version: "v1alpha1",
5354
Kind: "Test2Baz",
5455
}),
55-
CRDForGVK(metav1.GroupVersionKind{
56+
CRDForGVK(schema.GroupVersionKind{
5657
Group: "test3.cnrm.cloud.google.com",
5758
Version: "v1alpha1",
5859
Kind: "Test3UserSpecifiedResourceIDKind",
5960
}),
60-
CRDForGVK(metav1.GroupVersionKind{
61+
CRDForGVK(schema.GroupVersionKind{
6162
Group: "test3.cnrm.cloud.google.com",
6263
Version: "v1alpha1",
6364
Kind: "Test3ServerGeneratedResourceIDKind",
6465
}),
65-
CRDForGVK(metav1.GroupVersionKind{
66+
CRDForGVK(schema.GroupVersionKind{
6667
Group: "test4.cnrm.cloud.google.com",
6768
Version: "v1alpha1",
6869
Kind: "Test4DCLResourceServerGeneratedResourceIDKind",
6970
}),
70-
CRDForGVK(metav1.GroupVersionKind{
71+
CRDForGVK(schema.GroupVersionKind{
7172
Group: "test4.cnrm.cloud.google.com",
7273
Version: "v1alpha1",
7374
Kind: "Test4DCLResourceUserSpecifiedResourceIDKind",
7475
}),
75-
CRDForGVK(metav1.GroupVersionKind{
76+
CRDForGVK(schema.GroupVersionKind{
7677
Group: "test5.cnrm.cloud.google.com",
7778
Version: "v1beta1",
7879
Kind: "TestKindWithObservedState",
@@ -85,12 +86,12 @@ func FakeCRDs() []*apiextensions.CustomResourceDefinition {
8586
// hierarchical resources (e.g. "Project")
8687
func FakeCRDsWithHierarchicalResources() []*apiextensions.CustomResourceDefinition {
8788
return append(FakeCRDs(),
88-
CRDForGVK(metav1.GroupVersionKind{
89+
CRDForGVK(schema.GroupVersionKind{
8990
Group: "resourcemanager.cnrm.cloud.google.com",
9091
Version: "v1beta1",
9192
Kind: "Project",
9293
}),
93-
CRDForGVK(metav1.GroupVersionKind{
94+
CRDForGVK(schema.GroupVersionKind{
9495
Group: "resourcemanager.cnrm.cloud.google.com",
9596
Version: "v1beta1",
9697
Kind: "Folder",
@@ -464,7 +465,7 @@ func EnsureObjectExists(t *testing.T, obj *unstructured.Unstructured, c client.C
464465
}
465466
}
466467

467-
func CRDForGVK(gvk metav1.GroupVersionKind) *apiextensions.CustomResourceDefinition {
468+
func CRDForGVK(gvk schema.GroupVersionKind) *apiextensions.CustomResourceDefinition {
468469
singular := strings.ToLower(gvk.Kind)
469470
plural := text.Pluralize(singular)
470471
preserveUnknownFields := true

0 commit comments

Comments
 (0)