...

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

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

     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 partialpolicy_test
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"log"
    24  	"math/rand"
    25  	"reflect"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	iamv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/iam/v1beta1"
    31  	kcciamclient "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/iamclient"
    32  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/iam/partialpolicy"
    33  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/clientconfig"
    34  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/conversion"
    35  	dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
    36  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/schema/dclschemaloader"
    37  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
    38  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
    39  	testcontroller "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/controller"
    40  	testreconciler "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/controller/reconciler"
    41  	testgcp "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/gcp"
    42  	testiam "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/iam"
    43  	testk8s "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/k8s"
    44  	testmain "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/main"
    45  	testservicemappingloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/servicemappingloader"
    46  	tfprovider "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/tf/provider"
    47  
    48  	"github.com/google/go-cmp/cmp"
    49  	"k8s.io/apimachinery/pkg/api/errors"
    50  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    51  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    52  	"sigs.k8s.io/controller-runtime/pkg/client"
    53  	"sigs.k8s.io/controller-runtime/pkg/manager"
    54  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    55  )
    56  
    57  type updateTestCase struct {
    58  	name        string
    59  	newBindings []iamv1beta1.IAMPartialPolicyBinding
    60  }
    61  
    62  var (
    63  	mgr                     manager.Manager
    64  	expectedReconcileResult = reconcile.Result{RequeueAfter: k8s.MeanReconcileReenqueuePeriod}
    65  )
    66  
    67  var resourceLevelIAMPartialPolicyTestFunc = func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef iamv1beta1.ResourceReference) {
    68  	provider := tfprovider.NewOrLogFatal(tfprovider.DefaultConfig)
    69  	kubeClient := mgr.GetClient()
    70  	smLoader := testservicemappingloader.New(t)
    71  	dclSchemaLoader, err := dclschemaloader.New()
    72  	dclConfig := clientconfig.NewForIntegrationTest()
    73  	if err != nil {
    74  		t.Fatalf("error creating a new DCL schema loader: %v", err)
    75  	}
    76  	serviceMetaLoader := dclmetadata.New()
    77  	converter := conversion.New(dclSchemaLoader, serviceMetaLoader)
    78  	iamClient := kcciamclient.New(provider, smLoader, kubeClient, converter, dclConfig)
    79  	reconciler := testreconciler.NewForDCLAndTFTestReconciler(t, mgr, provider, dclConfig)
    80  
    81  	// Create two service accounts to construct different update cases
    82  	serviceAccountName1 := fmt.Sprintf("%v-%v", "sa1", rand.Intn(1000000))
    83  	createIAMServiceAccount(t, serviceAccountName1, refResource.GetNamespace(), kubeClient, reconciler)
    84  	defer deleteIAMServiceAccount(t, serviceAccountName1, refResource.GetNamespace(), kubeClient, reconciler)
    85  
    86  	serviceAccountName2 := fmt.Sprintf("%v-%v", "sa2", rand.Intn(1000000))
    87  	createIAMServiceAccount(t, serviceAccountName2, refResource.GetNamespace(), kubeClient, reconciler)
    88  	defer deleteIAMServiceAccount(t, serviceAccountName2, refResource.GetNamespace(), kubeClient, reconciler)
    89  
    90  	testMembers := []iamv1beta1.IAMPartialPolicyMember{
    91  		{
    92  			Member: iamv1beta1.Member("group:configconnector-test@google.com"),
    93  		},
    94  		{
    95  			MemberFrom: &iamv1beta1.MemberSource{
    96  				ServiceAccountRef: &iamv1beta1.MemberReference{
    97  					Name: serviceAccountName1,
    98  				},
    99  			},
   100  		},
   101  	}
   102  	bindings := make([]iamv1beta1.IAMPartialPolicyBinding, 0)
   103  	// Use PubSubTopic resource to test the case where existing IAM policy is empty.
   104  	if rc.Kind != "PubSubTopic" {
   105  		bindings = append(bindings, iamv1beta1.IAMPartialPolicyBinding{
   106  			Role:    rc.CreateBindingRole,
   107  			Members: testMembers,
   108  		})
   109  	}
   110  
   111  	updateTestCases := []updateTestCase{
   112  		{
   113  			name: "new bindings with one more role",
   114  			newBindings: []iamv1beta1.IAMPartialPolicyBinding{
   115  				{
   116  					Role:    rc.CreateBindingRole,
   117  					Members: testMembers,
   118  				},
   119  				{
   120  					Role:    rc.UpdateBindingRole,
   121  					Members: testMembers,
   122  				},
   123  			},
   124  		},
   125  		{
   126  			name: "new bindings with updated member",
   127  			newBindings: []iamv1beta1.IAMPartialPolicyBinding{
   128  				{
   129  					Role:    rc.CreateBindingRole,
   130  					Members: testMembers,
   131  				},
   132  				{
   133  					Role: rc.UpdateBindingRole,
   134  					Members: []iamv1beta1.IAMPartialPolicyMember{
   135  						{
   136  							Member: iamv1beta1.Member("group:kcc-team@google.com"),
   137  						},
   138  						{
   139  							MemberFrom: &iamv1beta1.MemberSource{
   140  								ServiceAccountRef: &iamv1beta1.MemberReference{
   141  									Name: serviceAccountName1,
   142  								},
   143  							},
   144  						},
   145  					},
   146  				},
   147  			},
   148  		},
   149  		{
   150  			name: "new bindings with added member",
   151  			newBindings: []iamv1beta1.IAMPartialPolicyBinding{
   152  				{
   153  					Role:    rc.CreateBindingRole,
   154  					Members: testMembers,
   155  				},
   156  				{
   157  					Role: rc.UpdateBindingRole,
   158  					Members: []iamv1beta1.IAMPartialPolicyMember{
   159  						{
   160  							Member: iamv1beta1.Member("group:configconnector-test@google.com"),
   161  						},
   162  						{
   163  							Member: iamv1beta1.Member("group:kcc-team@google.com"),
   164  						},
   165  						{
   166  							MemberFrom: &iamv1beta1.MemberSource{
   167  								ServiceAccountRef: &iamv1beta1.MemberReference{
   168  									Name: serviceAccountName1,
   169  								},
   170  							},
   171  						},
   172  					},
   173  				},
   174  			},
   175  		},
   176  		{
   177  			name: "new bindings with updated memberFrom",
   178  			newBindings: []iamv1beta1.IAMPartialPolicyBinding{
   179  				{
   180  					Role:    rc.CreateBindingRole,
   181  					Members: testMembers,
   182  				},
   183  				{
   184  					Role: rc.UpdateBindingRole,
   185  					Members: []iamv1beta1.IAMPartialPolicyMember{
   186  						{
   187  							Member: iamv1beta1.Member("group:configconnector-test@google.com"),
   188  						},
   189  						{
   190  							MemberFrom: &iamv1beta1.MemberSource{
   191  								ServiceAccountRef: &iamv1beta1.MemberReference{
   192  									Name: serviceAccountName2,
   193  								},
   194  							},
   195  						},
   196  					},
   197  				},
   198  			},
   199  		},
   200  		{
   201  			name: "new bindings with added memberFrom",
   202  			newBindings: []iamv1beta1.IAMPartialPolicyBinding{
   203  				{
   204  					Role:    rc.CreateBindingRole,
   205  					Members: testMembers,
   206  				},
   207  				{
   208  					Role: rc.UpdateBindingRole,
   209  					Members: []iamv1beta1.IAMPartialPolicyMember{
   210  						{
   211  							Member: iamv1beta1.Member("group:configconnector-test@google.com"),
   212  						},
   213  						{
   214  							MemberFrom: &iamv1beta1.MemberSource{
   215  								ServiceAccountRef: &iamv1beta1.MemberReference{
   216  									Name: serviceAccountName1,
   217  								},
   218  							},
   219  						},
   220  						{
   221  							MemberFrom: &iamv1beta1.MemberSource{
   222  								ServiceAccountRef: &iamv1beta1.MemberReference{
   223  									Name: serviceAccountName2,
   224  								},
   225  							},
   226  						},
   227  					},
   228  				},
   229  			},
   230  		},
   231  		{
   232  			name: "new bindings with removed role",
   233  			newBindings: []iamv1beta1.IAMPartialPolicyBinding{
   234  				{
   235  					Role:    rc.CreateBindingRole,
   236  					Members: testMembers,
   237  				},
   238  			},
   239  		},
   240  	}
   241  
   242  	k8sPartialPolicy := newIAMPartialPolicyFixture(t, refResource, resourceRef, bindings)
   243  	// Preset some bindings to the IAM policy.
   244  	existingPolicy := presetPolicy(t, iamClient, rc, k8sPartialPolicy)
   245  	testReconcileResourceLevelCreateNoChangesUpdateDelete(t, kubeClient, k8sPartialPolicy, updateTestCases, existingPolicy, iamClient, reconciler)
   246  }
   247  
   248  func TestReconcileIAMPartialPolicyResourceLevelCreateNoChangesUpdateDelete(t *testing.T) {
   249  	testiam.RunResourceLevelTest(t, mgr, resourceLevelIAMPartialPolicyTestFunc, testiam.ShouldRunWithNoProjectKind)
   250  }
   251  
   252  func TestReconcileIAMPartialPolicyResourceLevelCreateNoChangesUpdateDeleteWithExternalRef(t *testing.T) {
   253  	testiam.RunResourceLevelTestWithExternalRef(t, mgr, resourceLevelIAMPartialPolicyTestFunc, testiam.ShouldRunAcquire)
   254  }
   255  
   256  // Preset some bindings and (if the test case is for Project or Folder) audit configs in the IAM policy.
   257  // This is to verify that IAMPartialPolicy resources can preserve the existing bindings and audit configs.
   258  func presetPolicy(t *testing.T, iamClient *kcciamclient.IAMClient, rc testiam.IAMResourceContext, k8sPartialPolicy *iamv1beta1.IAMPartialPolicy) *iamv1beta1.IAMPolicy {
   259  	existingBinding := []iamv1beta1.IAMPolicyBinding{
   260  		{
   261  			Role:    rc.CreateBindingRole,
   262  			Members: []iamv1beta1.Member{iamv1beta1.Member(testgcp.GetIAMPolicyBindingMember(t))},
   263  		},
   264  	}
   265  	k8sPolicy := partialpolicy.ToIAMPolicySkeleton(k8sPartialPolicy)
   266  	k8sPolicy.Spec.Bindings = existingBinding
   267  	if rc.Kind == "Project" || rc.Kind == "Folder" {
   268  		k8sPolicy.Spec.AuditConfigs = []iamv1beta1.IAMPolicyAuditConfig{
   269  			{
   270  				Service: "allServices",
   271  				AuditLogConfigs: []iamv1beta1.AuditLogConfig{
   272  					{
   273  						LogType: "DATA_WRITE",
   274  					},
   275  					{
   276  						LogType:         "DATA_READ",
   277  						ExemptedMembers: []iamv1beta1.Member{iamv1beta1.Member(testgcp.GetIAMPolicyBindingMember(t))},
   278  					},
   279  				},
   280  			},
   281  		}
   282  	}
   283  	existingPolicy, err := iamClient.SetPolicy(context.TODO(), k8sPolicy)
   284  	if err != nil {
   285  		t.Fatalf("error setting policy: %v", err)
   286  	}
   287  	return existingPolicy
   288  }
   289  
   290  func testReconcileResourceLevelCreateNoChangesUpdateDelete(t *testing.T, kubeClient client.Client, k8sPartialPolicy *iamv1beta1.IAMPartialPolicy, updateTestCases []updateTestCase, existingPolicy *iamv1beta1.IAMPolicy, iamClient *kcciamclient.IAMClient, reconciler *testreconciler.TestReconciler) {
   291  	testReconcileResourceLevelCreate(t, kubeClient, k8sPartialPolicy, existingPolicy, iamClient, reconciler)
   292  	testReconcileResourceLevelNoChanges(t, kubeClient, k8sPartialPolicy, iamClient, reconciler)
   293  	currentPartialPolicy := k8sPartialPolicy
   294  	for _, tc := range updateTestCases {
   295  		newK8sPartialPolicy := currentPartialPolicy.DeepCopy()
   296  		newK8sPartialPolicy.Spec.Bindings = tc.newBindings
   297  		t.Run(fmt.Sprintf("TestUpdate-%v", tc.name), func(t *testing.T) {
   298  			testReconcileResourceLevelUpdate(t, kubeClient, currentPartialPolicy, newK8sPartialPolicy, existingPolicy, iamClient, reconciler)
   299  		})
   300  		currentPartialPolicy = newK8sPartialPolicy
   301  	}
   302  	testReconcileResourceLevelDelete(t, kubeClient, currentPartialPolicy, existingPolicy, iamClient, reconciler)
   303  }
   304  
   305  func testReconcileResourceLevelCreate(t *testing.T, kubeClient client.Client, k8sPartialPolicy *iamv1beta1.IAMPartialPolicy, existingPolicy *iamv1beta1.IAMPolicy, iamClient *kcciamclient.IAMClient, reconciler *testreconciler.TestReconciler) {
   306  	if err := kubeClient.Create(context.TODO(), k8sPartialPolicy); err != nil {
   307  		t.Fatalf("error creating k8sPartialPolicy: %v", err)
   308  	}
   309  	preReconcileGeneration := k8sPartialPolicy.GetGeneration()
   310  	reconciler.ReconcileObjectMeta(k8sPartialPolicy.ObjectMeta, iamv1beta1.IAMPartialPolicyGVK.Kind, expectedReconcileResult, nil)
   311  	k8sPolicy := partialpolicy.ToIAMPolicySkeleton(k8sPartialPolicy)
   312  	gcpPolicy, err := iamClient.GetPolicy(context.TODO(), k8sPolicy)
   313  	if err != nil {
   314  		t.Fatalf("error retrieving GCP policy: %v", err)
   315  	}
   316  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPartialPolicy), k8sPartialPolicy); err != nil {
   317  		t.Fatalf("unexpected error getting k8s resource: %v", err)
   318  	}
   319  	assertPolicy(t, k8sPartialPolicy, existingPolicy, gcpPolicy, iamClient)
   320  	testcontroller.AssertReadyCondition(t, k8sPartialPolicy.Status.Conditions)
   321  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, iamv1beta1.IAMPartialPolicyGVK.Kind, &k8sPartialPolicy.ObjectMeta, k8s.UpToDate)
   322  	assertObservedGenerationEquals(t, k8sPartialPolicy, preReconcileGeneration)
   323  }
   324  
   325  func testReconcileResourceLevelUpdate(t *testing.T, kubeClient client.Client, k8sPartialPolicy, newK8sPartialPolicy *iamv1beta1.IAMPartialPolicy, existingPolicy *iamv1beta1.IAMPolicy, iamClient *kcciamclient.IAMClient, reconciler *testreconciler.TestReconciler) {
   326  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPartialPolicy), k8sPartialPolicy); err != nil {
   327  		t.Fatalf("unexpected error getting k8s resource: %v", err)
   328  	}
   329  	newK8sPartialPolicy.SetResourceVersion(k8sPartialPolicy.GetResourceVersion())
   330  	if err := kubeClient.Update(context.TODO(), newK8sPartialPolicy); err != nil {
   331  		t.Fatalf("error updating k8sPartialPolicy: %v", err)
   332  	}
   333  	preReconcileGeneration := newK8sPartialPolicy.GetGeneration()
   334  	reconciler.ReconcileObjectMeta(newK8sPartialPolicy.ObjectMeta, iamv1beta1.IAMPartialPolicyGVK.Kind, expectedReconcileResult, nil)
   335  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(newK8sPartialPolicy), newK8sPartialPolicy); err != nil {
   336  		t.Fatalf("unexpected error getting k8s resource: %v", err)
   337  	}
   338  	k8sPolicy := partialpolicy.ToIAMPolicySkeleton(newK8sPartialPolicy)
   339  	gcpPolicy, err := iamClient.GetPolicy(context.TODO(), k8sPolicy)
   340  	if err != nil {
   341  		t.Fatalf("error retrieving GCP policy: %v", err)
   342  	}
   343  	assertPolicy(t, newK8sPartialPolicy, existingPolicy, gcpPolicy, iamClient)
   344  	testcontroller.AssertReadyCondition(t, newK8sPartialPolicy.Status.Conditions)
   345  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, iamv1beta1.IAMPartialPolicyGVK.Kind, &newK8sPartialPolicy.ObjectMeta, k8s.UpToDate)
   346  	assertObservedGenerationEquals(t, newK8sPartialPolicy, preReconcileGeneration)
   347  }
   348  
   349  func testReconcileResourceLevelNoChanges(t *testing.T, kubeClient client.Client, k8sPartialPolicy *iamv1beta1.IAMPartialPolicy, iamClient *kcciamclient.IAMClient, reconciler *testreconciler.TestReconciler) {
   350  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPartialPolicy), k8sPartialPolicy); err != nil {
   351  		t.Fatalf("unexpected error getting k8s resource: %v", err)
   352  	}
   353  	preReconcileGeneration := k8sPartialPolicy.GetGeneration()
   354  	reconciler.ReconcileObjectMeta(k8sPartialPolicy.ObjectMeta, iamv1beta1.IAMPartialPolicyGVK.Kind, expectedReconcileResult, nil)
   355  	newK8sPartialPolicy := &iamv1beta1.IAMPartialPolicy{}
   356  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPartialPolicy), newK8sPartialPolicy); err != nil {
   357  		t.Fatalf("unexpected error getting k8s resource: %v", err)
   358  	}
   359  	if k8sPartialPolicy.GetResourceVersion() != newK8sPartialPolicy.GetResourceVersion() {
   360  		t.Errorf("reconcile that was expected to be a no-op resulted in a write to the API server")
   361  	}
   362  	assertObservedGenerationEquals(t, newK8sPartialPolicy, preReconcileGeneration)
   363  }
   364  
   365  func TestReconcileIAMPartialPolicyResourceLevelDeleteParentFirst(t *testing.T) {
   366  	testFunc := func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef iamv1beta1.ResourceReference) {
   367  		bindings := []iamv1beta1.IAMPartialPolicyBinding{
   368  			{
   369  				Role: rc.CreateBindingRole,
   370  				Members: []iamv1beta1.IAMPartialPolicyMember{
   371  					{
   372  						Member: iamv1beta1.Member(testgcp.GetIAMPolicyBindingMember(t)),
   373  					},
   374  				},
   375  			},
   376  		}
   377  		k8sPartialPolicy := newIAMPartialPolicyFixture(t, refResource, resourceRef, bindings)
   378  		testReconcileResourceLevelDeleteParentFirst(t, mgr, k8sPartialPolicy, refResource)
   379  	}
   380  	testiam.RunResourceLevelTest(t, mgr, testFunc, testiam.ShouldRunDeleteParentFirst)
   381  }
   382  
   383  func TestReconcileIAMPartialPolicyResourceLevelDeleteParentFirstWithExternalRef(t *testing.T) {
   384  	testFunc := func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef iamv1beta1.ResourceReference) {
   385  		bindings := []iamv1beta1.IAMPartialPolicyBinding{
   386  			{
   387  				Role: rc.CreateBindingRole,
   388  				Members: []iamv1beta1.IAMPartialPolicyMember{
   389  					{
   390  						Member: iamv1beta1.Member(testgcp.GetIAMPolicyBindingMember(t)),
   391  					},
   392  				},
   393  			},
   394  		}
   395  		k8sPartialPolicy := newIAMPartialPolicyFixture(t, refResource, resourceRef, bindings)
   396  		testReconcileResourceLevelDeleteParentFirst(t, mgr, k8sPartialPolicy, refResource)
   397  	}
   398  	testiam.RunResourceLevelTestWithExternalRef(t, mgr, testFunc, testiam.ShouldRunDeleteParentFirst)
   399  }
   400  
   401  func testReconcileResourceLevelDelete(t *testing.T, kubeClient client.Client, k8sPartialPolicy *iamv1beta1.IAMPartialPolicy, existingPolicy *iamv1beta1.IAMPolicy, iamClient *kcciamclient.IAMClient, reconciler *testreconciler.TestReconciler) {
   402  	if k8sPartialPolicy.Spec.ResourceReference.Kind == "StorageBucket" {
   403  		// Once removing roles/storage.admin role, the caller cannot get access to the storage bucket
   404  		// even if it's the owner of the project.
   405  		return
   406  	}
   407  	if err := kubeClient.Delete(context.TODO(), k8sPartialPolicy); err != nil {
   408  		t.Fatalf("error deleting k8sPartialPolicy: %v", err)
   409  	}
   410  	reconciler.ReconcileObjectMeta(k8sPartialPolicy.ObjectMeta, iamv1beta1.IAMPartialPolicyGVK.Kind, testreconciler.ExpectedRequeueReconcileStruct, nil)
   411  	k8sPolicy := partialpolicy.ToIAMPolicySkeleton(k8sPartialPolicy)
   412  	gcpPolicy, err := iamClient.GetPolicy(context.TODO(), k8sPolicy)
   413  	if err != nil {
   414  		t.Fatalf("error retrieving GCP policy: %v", err)
   415  	}
   416  	assertPolicy(t, k8sPartialPolicy, existingPolicy, gcpPolicy, iamClient)
   417  	testk8s.RemoveDeletionDefenderFinalizer(t, k8sPartialPolicy, iamv1beta1.IAMPartialPolicyGVK, kubeClient)
   418  	reconciler.ReconcileObjectMeta(k8sPartialPolicy.ObjectMeta, iamv1beta1.IAMPartialPolicyGVK.Kind, expectedReconcileResult, nil)
   419  	gcpPolicy, err = iamClient.GetPolicy(context.TODO(), k8sPolicy)
   420  	if err != nil {
   421  		t.Fatalf("error retrieving GCP policy: %v", err)
   422  	}
   423  	testiam.AssertSamePolicy(t, existingPolicy, gcpPolicy)
   424  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPartialPolicy), k8sPartialPolicy); err == nil || !errors.IsNotFound(err) {
   425  		t.Fatalf("unexpected error value: %v", err)
   426  	}
   427  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, iamv1beta1.IAMPartialPolicyGVK.Kind, &k8sPartialPolicy.ObjectMeta, k8s.Deleted)
   428  }
   429  
   430  func testReconcileResourceLevelDeleteParentFirst(t *testing.T, mgr manager.Manager, k8sPartialPolicy *iamv1beta1.IAMPartialPolicy, refResource *unstructured.Unstructured) {
   431  	kubeClient := mgr.GetClient()
   432  	if err := kubeClient.Create(context.TODO(), k8sPartialPolicy); err != nil {
   433  		t.Fatalf("error creating k8sPartialPolicy: %v", err)
   434  	}
   435  	reconciler := testreconciler.New(t, mgr, tfprovider.NewOrLogFatal(tfprovider.DefaultConfig))
   436  	reconciler.ReconcileObjectMeta(k8sPartialPolicy.ObjectMeta, iamv1beta1.IAMPartialPolicyGVK.Kind, expectedReconcileResult, nil)
   437  
   438  	// First, delete the parent resource of the IAM Policy.
   439  	log.Printf("Deleting the parent of the IAM Policy first %v: %v/%v\n", refResource.GetKind(), refResource.GetNamespace(), refResource.GetName())
   440  	testk8s.RemoveDeletionDefenderFinalizerForUnstructured(t, refResource, kubeClient)
   441  	err := kubeClient.Delete(context.TODO(), refResource)
   442  	if err != nil {
   443  		t.Errorf("error deleting %v: %v", refResource, err)
   444  	}
   445  	reconciler.Reconcile(refResource, expectedReconcileResult, nil)
   446  
   447  	// Then, delete the IAM Policy.
   448  	testk8s.RemoveDeletionDefenderFinalizer(t, k8sPartialPolicy, iamv1beta1.IAMPartialPolicyGVK, kubeClient)
   449  	if err := kubeClient.Delete(context.TODO(), k8sPartialPolicy); err != nil {
   450  		t.Fatalf("error deleting k8sPartialPolicy: %v", err)
   451  	}
   452  	reconciler.ReconcileObjectMeta(k8sPartialPolicy.ObjectMeta, iamv1beta1.IAMPartialPolicyGVK.Kind, expectedReconcileResult, nil)
   453  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPartialPolicy), k8sPartialPolicy); err == nil || !errors.IsNotFound(err) {
   454  		t.Fatalf("unexpected error value: %v", err)
   455  	}
   456  	// Wait till all the events are properly cached.
   457  	testcontroller.CollectEvents(t, mgr.GetConfig(), k8sPartialPolicy.Namespace, 6, 5*time.Second)
   458  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, iamv1beta1.IAMPartialPolicyGVK.Kind, &k8sPartialPolicy.ObjectMeta, k8s.Deleted)
   459  }
   460  
   461  func TestReconcileIAMPartialPolicyResourceLevelAcquire(t *testing.T) {
   462  	testFunc := func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef iamv1beta1.ResourceReference) {
   463  		bindings := []iamv1beta1.IAMPartialPolicyBinding{
   464  			{
   465  				Role: rc.CreateBindingRole,
   466  				Members: []iamv1beta1.IAMPartialPolicyMember{
   467  					{
   468  						Member: iamv1beta1.Member(testgcp.GetIAMPolicyBindingMember(t)),
   469  					},
   470  				},
   471  			},
   472  		}
   473  		existingBindings := []iamv1beta1.IAMPolicyBinding{
   474  			{
   475  				Role: rc.CreateBindingRole,
   476  				Members: []iamv1beta1.Member{
   477  					iamv1beta1.Member(testgcp.GetIAMPolicyBindingMember(t)),
   478  				},
   479  			},
   480  		}
   481  		k8sPartialPolicy := newIAMPartialPolicyFixture(t, refResource, resourceRef, bindings)
   482  		testReconcileResourceLevelAcquire(t, mgr, k8sPartialPolicy, existingBindings)
   483  	}
   484  	testiam.RunResourceLevelTest(t, mgr, testFunc, testiam.ShouldRunAcquire)
   485  }
   486  
   487  func TestReconcileIAMPartialPolicyResourceLevelAcquireWithExternalRef(t *testing.T) {
   488  	testFunc := func(t *testing.T, _ string, mgr manager.Manager, rc testiam.IAMResourceContext, refResource *unstructured.Unstructured, resourceRef iamv1beta1.ResourceReference) {
   489  		bindings := []iamv1beta1.IAMPartialPolicyBinding{
   490  			{
   491  				Role: rc.CreateBindingRole,
   492  				Members: []iamv1beta1.IAMPartialPolicyMember{
   493  					{
   494  						Member: iamv1beta1.Member(testgcp.GetIAMPolicyBindingMember(t)),
   495  					},
   496  				},
   497  			},
   498  		}
   499  		existingBindings := []iamv1beta1.IAMPolicyBinding{
   500  			{
   501  				Role: rc.CreateBindingRole,
   502  				Members: []iamv1beta1.Member{
   503  					iamv1beta1.Member(testgcp.GetIAMPolicyBindingMember(t)),
   504  				},
   505  			},
   506  		}
   507  		k8sPartialPolicy := newIAMPartialPolicyFixture(t, refResource, resourceRef, bindings)
   508  		testReconcileResourceLevelAcquire(t, mgr, k8sPartialPolicy, existingBindings)
   509  	}
   510  	testiam.RunResourceLevelTestWithExternalRef(t, mgr, testFunc, testiam.ShouldRunAcquire)
   511  }
   512  
   513  func testReconcileResourceLevelAcquire(t *testing.T, mgr manager.Manager, k8sPartialPolicy *iamv1beta1.IAMPartialPolicy, existingBindings []iamv1beta1.IAMPolicyBinding) {
   514  	kubeClient := mgr.GetClient()
   515  	provider := tfprovider.NewOrLogFatal(tfprovider.DefaultConfig)
   516  	smLoader := testservicemappingloader.New(t)
   517  	dclConfig := clientconfig.NewForIntegrationTest()
   518  	dclSchemaLoader, err := dclschemaloader.New()
   519  	if err != nil {
   520  		t.Fatalf("error creating a new DCL schema loader: %v", err)
   521  	}
   522  	serviceMetaLoader := dclmetadata.New()
   523  	converter := conversion.New(dclSchemaLoader, serviceMetaLoader)
   524  	iamClient := kcciamclient.New(provider, smLoader, kubeClient, converter, dclConfig)
   525  	reconciler := testreconciler.New(t, mgr, provider)
   526  
   527  	// Create resource in GCP
   528  	k8sPolicy := partialpolicy.ToIAMPolicySkeleton(k8sPartialPolicy)
   529  	k8sPolicy.Spec.Bindings = existingBindings
   530  	if _, err := iamClient.SetPolicy(context.TODO(), k8sPolicy); err != nil {
   531  		t.Fatalf("error creating GCP policy: %v", err)
   532  	}
   533  
   534  	// Acquire IAM Policy
   535  	if err := kubeClient.Create(context.TODO(), k8sPartialPolicy); err != nil {
   536  		t.Fatalf("error creating k8sPartialPolicy: %v", err)
   537  	}
   538  	preReconcileGeneration := k8sPartialPolicy.GetGeneration()
   539  	reconciler.ReconcileObjectMeta(k8sPartialPolicy.ObjectMeta, iamv1beta1.IAMPartialPolicyGVK.Kind, expectedReconcileResult, nil)
   540  	gcpPolicy, err := iamClient.GetPolicy(context.TODO(), k8sPolicy)
   541  	if err != nil {
   542  		t.Fatalf("error retrieving GCP policy: %v", err)
   543  	}
   544  	testiam.AssertSamePolicy(t, k8sPolicy, gcpPolicy)
   545  	if err := kubeClient.Get(context.TODO(), k8s.GetNamespacedName(k8sPartialPolicy), k8sPartialPolicy); err != nil {
   546  		t.Fatalf("unexpected error getting k8s resource: %v", err)
   547  	}
   548  	testcontroller.AssertReadyCondition(t, k8sPartialPolicy.Status.Conditions)
   549  	testcontroller.AssertEventRecordedForObjectMetaAndKind(t, kubeClient, iamv1beta1.IAMPartialPolicyGVK.Kind, &k8sPartialPolicy.ObjectMeta, k8s.UpToDate)
   550  	assertObservedGenerationEquals(t, k8sPartialPolicy, preReconcileGeneration)
   551  }
   552  
   553  func newIAMPartialPolicyFixture(t *testing.T, refResource *unstructured.Unstructured, resourceRef iamv1beta1.ResourceReference, bindings []iamv1beta1.IAMPartialPolicyBinding) *iamv1beta1.IAMPartialPolicy {
   554  	t.Helper()
   555  	if !strings.HasPrefix(t.Name(), "TestReconcile") {
   556  		t.Fatalf("Unexpected test name prefix, all tests are expected to start with TestReconcile")
   557  	}
   558  	return &iamv1beta1.IAMPartialPolicy{
   559  		TypeMeta: metav1.TypeMeta{
   560  			APIVersion: iamv1beta1.IAMPartialPolicyGVK.GroupVersion().String(),
   561  			Kind:       iamv1beta1.IAMPartialPolicyGVK.Kind,
   562  		},
   563  		ObjectMeta: metav1.ObjectMeta{
   564  			Name:      testcontroller.UniqueName(t, name(t)),
   565  			Namespace: refResource.GetNamespace(),
   566  		},
   567  		Spec: iamv1beta1.IAMPartialPolicySpec{
   568  			ResourceReference: resourceRef,
   569  			Bindings:          bindings,
   570  		},
   571  	}
   572  }
   573  
   574  func createIAMServiceAccount(t *testing.T, name, namespace string, kubeClient client.Client, reconciler *testreconciler.TestReconciler) {
   575  	refServiceAccount := test.NewIAMServiceAccountUnstructured(name, namespace)
   576  	if err := kubeClient.Create(context.TODO(), refServiceAccount); err != nil {
   577  		t.Fatalf("error creating IAMServiceAccount: %v", err)
   578  	}
   579  	objectMeta := metav1.ObjectMeta{
   580  		Name:      name,
   581  		Namespace: namespace,
   582  	}
   583  	reconciler.ReconcileObjectMeta(objectMeta, kcciamclient.IAMServiceAccountGVK.Kind, expectedReconcileResult, nil)
   584  }
   585  
   586  func deleteIAMServiceAccount(t *testing.T, name, namespace string, kubeClient client.Client, reconciler *testreconciler.TestReconciler) {
   587  	refServiceAccount := test.NewIAMServiceAccountUnstructured(name, namespace)
   588  
   589  	testk8s.RemoveDeletionDefenderFinalizerForUnstructured(t, refServiceAccount, kubeClient)
   590  	if err := kubeClient.Delete(context.TODO(), refServiceAccount); err != nil {
   591  		t.Fatalf("error deleting IAMServiceAccount %v: %v", refServiceAccount.GetName(), err)
   592  	}
   593  	reconciler.Reconcile(refServiceAccount, expectedReconcileResult, nil)
   594  }
   595  
   596  func assertPolicy(t *testing.T, k8sPartialPolicy *iamv1beta1.IAMPartialPolicy, existingPolicy *iamv1beta1.IAMPolicy, gcpPolicy *iamv1beta1.IAMPolicy, iamClient *kcciamclient.IAMClient) {
   597  	if !reflect.DeepEqual(k8sPartialPolicy.Spec.ResourceReference, gcpPolicy.Spec.ResourceReference) {
   598  		diff := cmp.Diff(k8sPartialPolicy.Spec.ResourceReference, gcpPolicy.Spec.ResourceReference)
   599  		t.Fatalf("GCP policy has incorrect resource reference. Diff (-want, +got):\n%v", diff)
   600  	}
   601  	if !testiam.ContainsBindings(gcpPolicy.Spec.Bindings, existingPolicy.Spec.Bindings) {
   602  		t.Fatalf("GCP policy doesn't have all existing bindings as expected; current bindings: %v, existing bindings: %v", gcpPolicy.Spec.Bindings, existingPolicy.Spec.Bindings)
   603  	}
   604  	resolver := partialpolicy.IAMMemberIdentityResolver{Iamclient: iamClient, Ctx: context.TODO()}
   605  	configuredBinding, err := partialpolicy.ConvertIAMPartialBindingsToIAMPolicyBindings(k8sPartialPolicy, &resolver)
   606  	if err != nil {
   607  		t.Fatalf("ConvertIAMPartialBindingsToIAMPolicyBindings returned error: %v", err)
   608  	}
   609  	if !testiam.ContainsBindings(gcpPolicy.Spec.Bindings, configuredBinding) {
   610  		t.Fatalf("GCP policy doesn't have all bindings that are configured in IAM partial policy; current bindings: %v, configured bindings: %v", gcpPolicy.Spec.Bindings, configuredBinding)
   611  	}
   612  	if !testiam.SameBindings(k8sPartialPolicy.Status.AllBindings, gcpPolicy.Spec.Bindings) {
   613  		t.Fatalf("GCP policy has incorrect bindings; got: %v, want: %v", gcpPolicy.Spec.Bindings, k8sPartialPolicy.Spec.Bindings)
   614  	}
   615  	if !testiam.SameAuditConfigs(existingPolicy.Spec.AuditConfigs, gcpPolicy.Spec.AuditConfigs) {
   616  		t.Fatalf("GCP policy has incorrect audit configs; got: %v, want: %v", gcpPolicy.Spec.AuditConfigs, existingPolicy.Spec.AuditConfigs)
   617  	}
   618  }
   619  
   620  func assertObservedGenerationEquals(t *testing.T, gcpPolicy *iamv1beta1.IAMPartialPolicy, preReconcileGeneration int64) {
   621  	if gcpPolicy.Status.ObservedGeneration != preReconcileGeneration {
   622  		t.Errorf("observedGeneration %v doesn't match with the pre-reconcile generation %v", gcpPolicy.Status.ObservedGeneration, preReconcileGeneration)
   623  	}
   624  }
   625  
   626  func name(t *testing.T) string {
   627  	// Necessary to remove the "/$KIND" portion of the subtest name
   628  	name := strings.ToLower(testcontroller.Name(t))
   629  	return strings.Split(name, "/")[0]
   630  }
   631  
   632  func TestMain(m *testing.M) {
   633  	testmain.TestMainForIntegrationTests(m, &mgr)
   634  }
   635  

View as plain text