...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/policymember/iampolicymember_controller_integration_test.go

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

     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  //go:build integration
    16  // +build integration
    17  
    18  package policymember_test
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"log"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/iam/v1beta1"
    29  	kcciamclient "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/iamclient"
    30  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/policymember"
    31  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/clientconfig"
    32  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/conversion"
    33  	dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
    34  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/schema/dclschemaloader"
    35  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
    36  	testcontroller "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/controller"
    37  	testreconciler "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/controller/reconciler"
    38  	testgcp "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/gcp"
    39  	testiam "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/iam"
    40  	testk8s "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/k8s"
    41  	testmain "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/main"
    42  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/resourcefixture"
    43  	testservicemappingloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/servicemappingloader"
    44  	tfprovider "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/tf/provider"
    45  
    46  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    47  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    48  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    49  	"sigs.k8s.io/controller-runtime/pkg/manager"
    50  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    51  )
    52  
    53  var (
    54  	mgr                     manager.Manager
    55  	expectedReconcileResult = reconcile.Result{RequeueAfter: k8s.MeanReconcileReenqueuePeriod}
    56  )
    57  
    58  func TestReconcileIAMPolicyMemberResourceLevelCreateDelete(t *testing.T) {
    59  	testFunc := func(t *testing.T, testId string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef v1beta1.ResourceReference) {
    60  		k8sPolicyMember := newIAMPolicyMemberFixture(t, refResource, resourceRef, rc.CreateBindingRole, testgcp.GetIAMPolicyBindingMember(t))
    61  		testPolicyMemberCreateDelete(t, mgr, k8sPolicyMember)
    62  	}
    63  	testiam.RunResourceLevelTest(t, mgr, testFunc, testiam.ShouldRunWithTFResourcesOnly)
    64  }
    65  
    66  func TestReconcileIAMPolicyMemberResourceLevelCreateDeleteWithReconcileInterval(t *testing.T) {
    67  	shouldRun := func(fixture resourcefixture.ResourceFixture) bool {
    68  		return fixture.GVK.Kind == "PubSubTopic"
    69  	}
    70  	testFunc := func(t *testing.T, testId string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef v1beta1.ResourceReference) {
    71  		k8sPolicyMember := newIAMPolicyMemberFixture(t, refResource, resourceRef, rc.CreateBindingRole, testgcp.GetIAMPolicyBindingMember(t))
    72  		k8sPolicyMember.SetAnnotations(map[string]string{k8s.ReconcileIntervalInSecondsAnnotation: "5"})
    73  		testPolicyMemberCreateDelete(t, mgr, k8sPolicyMember)
    74  	}
    75  	testiam.RunResourceLevelTest(t, mgr, testFunc, shouldRun)
    76  }
    77  
    78  func TestReconcileIAMPolicyMemberResourceLevelCreateDeleteWithExternalRef(t *testing.T) {
    79  	testFunc := func(t *testing.T, testId string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef v1beta1.ResourceReference) {
    80  		k8sPolicyMember := newIAMPolicyMemberFixture(t, refResource, resourceRef, rc.CreateBindingRole, testgcp.GetIAMPolicyBindingMember(t))
    81  		testPolicyMemberCreateDelete(t, mgr, k8sPolicyMember)
    82  	}
    83  	testiam.RunResourceLevelTestWithExternalRef(t, mgr, testFunc, testiam.ShouldRunWithExternalRef)
    84  }
    85  
    86  func TestReconcileIAMPolicyMemberResourceLevelDeleteParentFirst(t *testing.T) {
    87  	shouldRun := func(fixture resourcefixture.ResourceFixture) bool {
    88  		return fixture.GVK.Kind == "PubSubTopic"
    89  	}
    90  	testFunc := func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef v1beta1.ResourceReference) {
    91  		k8sPolicyMember := newIAMPolicyMemberFixture(t, refResource, resourceRef, rc.CreateBindingRole, testgcp.GetIAMPolicyBindingMember(t))
    92  		testReconcileResourceLevelDeleteParentFirst(t, mgr, k8sPolicyMember, refResource)
    93  	}
    94  	testiam.RunResourceLevelTest(t, mgr, testFunc, shouldRun)
    95  }
    96  
    97  func TestReconcileIAMPolicyMemberResourceLevelDeleteParentFirstWithExternalRef(t *testing.T) {
    98  	shouldRun := func(fixture resourcefixture.ResourceFixture) bool {
    99  		return fixture.GVK.Kind == "PubSubTopic"
   100  	}
   101  	testFunc := func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef v1beta1.ResourceReference) {
   102  		k8sPolicyMember := newIAMPolicyMemberFixture(t, refResource, resourceRef, rc.CreateBindingRole, testgcp.GetIAMPolicyBindingMember(t))
   103  		testReconcileResourceLevelDeleteParentFirst(t, mgr, k8sPolicyMember, refResource)
   104  	}
   105  	testiam.RunResourceLevelTestWithExternalRef(t, mgr, testFunc, shouldRun)
   106  }
   107  
   108  func TestReconcileIAMPolicyMemberResourceLevelAcquire(t *testing.T) {
   109  	shouldRun := func(fixture resourcefixture.ResourceFixture) bool {
   110  		return fixture.GVK.Kind == "PubSubTopic"
   111  	}
   112  	testFunc := func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef v1beta1.ResourceReference) {
   113  		k8sPolicyMember := newIAMPolicyMemberFixture(t, refResource, resourceRef, rc.CreateBindingRole, testgcp.GetIAMPolicyBindingMember(t))
   114  		testReconcileResourceLevelAcquire(t, mgr, k8sPolicyMember)
   115  	}
   116  	testiam.RunResourceLevelTest(t, mgr, testFunc, shouldRun)
   117  }
   118  
   119  func TestReconcileIAMPolicyMemberResourceLevelAcquireWithExternalRef(t *testing.T) {
   120  	shouldRun := func(fixture resourcefixture.ResourceFixture) bool {
   121  		return fixture.GVK.Kind == "PubSubTopic"
   122  	}
   123  	testFunc := func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef v1beta1.ResourceReference) {
   124  		k8sPolicyMember := newIAMPolicyMemberFixture(t, refResource, resourceRef, rc.CreateBindingRole, testgcp.GetIAMPolicyBindingMember(t))
   125  		testReconcileResourceLevelAcquire(t, mgr, k8sPolicyMember)
   126  	}
   127  	testiam.RunResourceLevelTestWithExternalRef(t, mgr, testFunc, shouldRun)
   128  }
   129  
   130  func testPolicyMemberCreateDelete(t *testing.T, mgr manager.Manager, k8sPolicyMember *v1beta1.IAMPolicyMember) {
   131  	kubeClient := mgr.GetClient()
   132  	provider := tfprovider.NewOrLogFatal(tfprovider.DefaultConfig)
   133  	smLoader := testservicemappingloader.New(t)
   134  	dclSchemaLoader, err := dclschemaloader.New()
   135  	dclConfig := clientconfig.NewForIntegrationTest()
   136  	if err != nil {
   137  		t.Fatalf("error creating a new DCL schema loader: %v", err)
   138  	}
   139  	serviceMetaLoader := dclmetadata.New()
   140  	converter := conversion.New(dclSchemaLoader, serviceMetaLoader)
   141  	iamClient := kcciamclient.New(provider, smLoader, kubeClient, converter, dclConfig)
   142  	_, err = iamClient.GetPolicyMember(context.TODO(), k8sPolicyMember)
   143  	if !errors.Is(err, kcciamclient.NotFoundError) {
   144  		t.Fatalf("unexpected error value: got '%v', want '%v'", err, kcciamclient.NotFoundError)
   145  	}
   146  	if err := kubeClient.Create(context.TODO(), k8sPolicyMember); err != nil {
   147  		t.Fatalf("error creating policy member: %v", err)
   148  	}
   149  	preReconcileGeneration := k8sPolicyMember.GetGeneration()
   150  	reconciler := testreconciler.New(t, mgr, tfprovider.NewOrLogFatal(tfprovider.DefaultConfig))
   151  	resource, err := policymember.ToK8sResource(k8sPolicyMember)
   152  	if err != nil {
   153  		t.Fatalf("error converting object %v to k8sResource: %v", k8sPolicyMember, err)
   154  	}
   155  	u, err := resource.MarshalAsUnstructured()
   156  	if err != nil {
   157  		t.Fatalf("error marshalling object %v as unstructured: %v", k8sPolicyMember, err)
   158  	}
   159  	reconciler.ReconcileObjectMeta(k8sPolicyMember.ObjectMeta, v1beta1.IAMPolicyMemberGVK.Kind, testreconciler.ExpectedSuccessfulReconcileResultFor(reconciler, u), nil)
   160  	gcpPolicyMember, err := iamClient.GetPolicyMember(context.TODO(), k8sPolicyMember)
   161  	if err != nil {
   162  		t.Fatalf("unexpected error getting policy member: %v", err)
   163  	}
   164  	if gcpPolicyMember.Spec.Role != k8sPolicyMember.Spec.Role {
   165  		t.Errorf("unexpected value for role: got '%v', want '%v'", gcpPolicyMember.Spec.Role, k8sPolicyMember.Spec.Role)
   166  	}
   167  	if gcpPolicyMember.Spec.Member != k8sPolicyMember.Spec.Member {
   168  		t.Errorf("unexpected value for role: got '%v', want '%v'", gcpPolicyMember.Spec.Member, k8sPolicyMember.Spec.Member)
   169  	}
   170  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPolicyMember), k8sPolicyMember); err != nil {
   171  		t.Fatalf("unexpected error getting resource: %v", err)
   172  	}
   173  	testcontroller.AssertReadyCondition(t, k8sPolicyMember.Status.Conditions)
   174  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, v1beta1.IAMPolicyMemberGVK.Kind, &k8sPolicyMember.ObjectMeta, k8s.UpToDate)
   175  	if err := kubeClient.Delete(context.TODO(), k8sPolicyMember); err != nil {
   176  		t.Fatalf("error deleting policy member: %v", err)
   177  	}
   178  	assertObservedGenerationEquals(t, k8sPolicyMember, preReconcileGeneration)
   179  	reconciler.ReconcileObjectMeta(k8sPolicyMember.ObjectMeta, v1beta1.IAMPolicyMemberGVK.Kind, testreconciler.ExpectedRequeueReconcileStruct, nil)
   180  	if _, err := iamClient.GetPolicyMember(context.TODO(), k8sPolicyMember); err != nil {
   181  		t.Fatalf("expected policy member to exist in GCP, but got error: %v", err)
   182  	}
   183  	testk8s.RemoveDeletionDefenderFinalizer(t, k8sPolicyMember, v1beta1.IAMPolicyMemberGVK, kubeClient)
   184  	reconciler.ReconcileObjectMeta(k8sPolicyMember.ObjectMeta, v1beta1.IAMPolicyMemberGVK.Kind, testreconciler.ExpectedSuccessfulReconcileResultFor(reconciler, u), nil)
   185  	gcpPolicyMember, err = iamClient.GetPolicyMember(context.TODO(), k8sPolicyMember)
   186  	if !errors.Is(err, kcciamclient.NotFoundError) {
   187  		t.Fatalf("unexpected error value: got '%v', want '%v'", err, kcciamclient.NotFoundError)
   188  	}
   189  	if gcpPolicyMember != nil {
   190  		t.Fatalf("unexpected value for policy member: got '%v', want '%v'", gcpPolicyMember, nil)
   191  	}
   192  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPolicyMember), k8sPolicyMember); err == nil || !apierrors.IsNotFound(err) {
   193  		t.Fatalf("unexpected error value: %v", err)
   194  	}
   195  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, v1beta1.IAMPolicyMemberGVK.Kind, &k8sPolicyMember.ObjectMeta, k8s.Deleted)
   196  }
   197  
   198  func testReconcileResourceLevelDeleteParentFirst(t *testing.T, mgr manager.Manager, k8sPolicyMember *v1beta1.IAMPolicyMember, refResource *unstructured.Unstructured) {
   199  	kubeClient := mgr.GetClient()
   200  	if err := kubeClient.Create(context.TODO(), k8sPolicyMember); err != nil {
   201  		t.Fatalf("error creating k8sPolicy: %v", err)
   202  	}
   203  	reconciler := testreconciler.New(t, mgr, tfprovider.NewOrLogFatal(tfprovider.DefaultConfig))
   204  	reconciler.ReconcileObjectMeta(k8sPolicyMember.ObjectMeta, v1beta1.IAMPolicyMemberGVK.Kind, expectedReconcileResult, nil)
   205  
   206  	// First, delete the parent resource of the IAM Policy.
   207  	log.Printf("Deleting the parent of the IAM Policy Member first %v: %v/%v\n", refResource.GetKind(), refResource.GetNamespace(), refResource.GetName())
   208  	testk8s.RemoveDeletionDefenderFinalizerForUnstructured(t, refResource, kubeClient)
   209  	err := kubeClient.Delete(context.TODO(), refResource)
   210  	if err != nil {
   211  		t.Errorf("error deleting %v: %v", refResource, err)
   212  	}
   213  	reconciler.Reconcile(refResource, expectedReconcileResult, nil)
   214  
   215  	// Then, delete the IAM Policy.
   216  	testk8s.RemoveDeletionDefenderFinalizer(t, k8sPolicyMember, v1beta1.IAMPolicyMemberGVK, kubeClient)
   217  	if err := kubeClient.Delete(context.TODO(), k8sPolicyMember); err != nil {
   218  		t.Fatalf("error deleting k8sPolicyMember: %v", err)
   219  	}
   220  	reconciler.ReconcileObjectMeta(k8sPolicyMember.ObjectMeta, v1beta1.IAMPolicyMemberGVK.Kind, expectedReconcileResult, nil)
   221  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPolicyMember), k8sPolicyMember); err == nil || !apierrors.IsNotFound(err) {
   222  		t.Fatalf("unexpected error value: %v", err)
   223  	}
   224  	// Wait till all the events are properly cached.
   225  	testcontroller.CollectEvents(t, mgr.GetConfig(), k8sPolicyMember.Namespace, 6, 5*time.Second)
   226  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, v1beta1.IAMPolicyMemberGVK.Kind, &k8sPolicyMember.ObjectMeta, k8s.Deleted)
   227  }
   228  
   229  func testReconcileResourceLevelAcquire(t *testing.T, mgr manager.Manager, k8sPolicyMember *v1beta1.IAMPolicyMember) {
   230  	kubeClient := mgr.GetClient()
   231  	provider := tfprovider.NewOrLogFatal(tfprovider.DefaultConfig)
   232  	smLoader := testservicemappingloader.New(t)
   233  	dclSchemaLoader, err := dclschemaloader.New()
   234  	dclConfig := clientconfig.NewForIntegrationTest()
   235  	if err != nil {
   236  		t.Fatalf("error creating a new DCL schema loader: %v", err)
   237  	}
   238  	serviceMetaLoader := dclmetadata.New()
   239  	converter := conversion.New(dclSchemaLoader, serviceMetaLoader)
   240  	iamClient := kcciamclient.New(provider, smLoader, kubeClient, converter, dclConfig)
   241  	reconciler := testreconciler.New(t, mgr, provider)
   242  
   243  	// Create resource in GCP
   244  	if _, err := iamClient.SetPolicyMember(context.TODO(), k8sPolicyMember); err != nil {
   245  		t.Fatalf("error creating GCP policy member: %v", err)
   246  	}
   247  
   248  	// Acquire IAM Policy Member
   249  	if err := kubeClient.Create(context.TODO(), k8sPolicyMember); err != nil {
   250  		t.Fatalf("error creating k8sPolicy: %v", err)
   251  	}
   252  	preReconcileGeneration := k8sPolicyMember.GetGeneration()
   253  	reconciler.ReconcileObjectMeta(k8sPolicyMember.ObjectMeta, v1beta1.IAMPolicyMemberGVK.Kind, expectedReconcileResult, nil)
   254  	if _, err := iamClient.GetPolicyMember(context.TODO(), k8sPolicyMember); err != nil {
   255  		t.Fatalf("unexpected error getting policy member: %v", err)
   256  	}
   257  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPolicyMember), k8sPolicyMember); err != nil {
   258  		t.Fatalf("unexpected error getting k8s resource: %v", err)
   259  	}
   260  	testcontroller.AssertReadyCondition(t, k8sPolicyMember.Status.Conditions)
   261  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, v1beta1.IAMPolicyMemberGVK.Kind, &k8sPolicyMember.ObjectMeta, k8s.UpToDate)
   262  	assertObservedGenerationEquals(t, k8sPolicyMember, preReconcileGeneration)
   263  }
   264  
   265  func assertObservedGenerationEquals(t *testing.T, k8sPolicyMember *v1beta1.IAMPolicyMember, preReconcileGeneration int64) {
   266  	if k8sPolicyMember.Status.ObservedGeneration != preReconcileGeneration {
   267  		t.Errorf("observedGeneration %v doesn't match with the pre-reconcile generation %v", k8sPolicyMember.Status.ObservedGeneration, preReconcileGeneration)
   268  	}
   269  }
   270  
   271  func newIAMPolicyMemberFixture(t *testing.T, refResource *unstructured.Unstructured, resourceRef v1beta1.ResourceReference, role, member string) *v1beta1.IAMPolicyMember {
   272  	return &v1beta1.IAMPolicyMember{
   273  		TypeMeta: metav1.TypeMeta{
   274  			APIVersion: v1beta1.IAMPolicyMemberGVK.GroupVersion().String(),
   275  			Kind:       v1beta1.IAMPolicyMemberGVK.Kind,
   276  		},
   277  		ObjectMeta: metav1.ObjectMeta{
   278  			Name:      testcontroller.UniqueName(t, name(t)),
   279  			Namespace: refResource.GetNamespace(),
   280  		},
   281  		Spec: v1beta1.IAMPolicyMemberSpec{
   282  			Role:              role,
   283  			Member:            v1beta1.Member(member),
   284  			ResourceReference: resourceRef,
   285  		},
   286  	}
   287  }
   288  
   289  func name(t *testing.T) string {
   290  	// Necessary to remove the "/$KIND" portion of the subtest name
   291  	name := strings.ToLower(testcontroller.Name(t))
   292  	return strings.Split(name, "/")[0]
   293  }
   294  
   295  func TestMain(m *testing.M) {
   296  	testmain.TestMainForIntegrationTests(m, &mgr)
   297  }
   298  

View as plain text