github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_appconfig_test.go (about)

     1  // Copyright (c) 2021, 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package mcagent
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	oamv1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
    13  	asserts "github.com/stretchr/testify/assert"
    14  	clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1"
    15  	"github.com/verrazzano/verrazzano/application-operator/constants"
    16  	clusterstest "github.com/verrazzano/verrazzano/application-operator/controllers/clusters/test"
    17  	vzconst "github.com/verrazzano/verrazzano/pkg/constants"
    18  	"go.uber.org/zap"
    19  	"k8s.io/apimachinery/pkg/api/errors"
    20  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    21  	"k8s.io/apimachinery/pkg/runtime"
    22  	"k8s.io/apimachinery/pkg/types"
    23  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    24  )
    25  
    26  var mcAppConfigExpectedLabels = map[string]string{"label1": "test1", vzconst.VerrazzanoManagedLabelKey: constants.LabelVerrazzanoManagedDefault}
    27  var mcAppConfigExpectedLabelsAfterUpdate = map[string]string{"label1": "test1updated", vzconst.VerrazzanoManagedLabelKey: constants.LabelVerrazzanoManagedDefault}
    28  
    29  // TestCreateMCAppConfig tests the synchronization method for the following use case.
    30  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
    31  // WHEN the new object exists
    32  // THEN ensure that the MultiClusterApplicationConfiguration and its associated OAM Component are created.
    33  func TestCreateMCAppConfig(t *testing.T) {
    34  	assert := asserts.New(t)
    35  	log := zap.S().With("test")
    36  
    37  	// Test data
    38  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
    39  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
    40  
    41  	testComponent, err := getSampleOamComponent("testdata/hello-component.yaml")
    42  	assert.NoError(err, "failed to read sample data for OAM Component")
    43  
    44  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testMCAppConfig, &testComponent).Build()
    45  
    46  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).Build()
    47  
    48  	// Make the request
    49  	s := &Syncer{
    50  		AdminClient:        adminClient,
    51  		LocalClient:        localClient,
    52  		Log:                log,
    53  		ManagedClusterName: testClusterName,
    54  		Context:            context.TODO(),
    55  	}
    56  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
    57  
    58  	// Validate the results
    59  	assert.NoError(err)
    60  
    61  	// Verify the associated OAM component got created on local cluster
    62  	component := &oamv1alpha2.Component{}
    63  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testComponent.Name, Namespace: testComponent.Namespace}, component)
    64  	assert.NoError(err)
    65  	assert.Equal(s.ManagedClusterName, component.Labels[managedClusterLabel])
    66  	assert.Equal(testMCAppConfig.Name, component.Labels[mcAppConfigsLabel])
    67  	assert.Equal(constants.LabelVerrazzanoManagedDefault, component.Labels[vzconst.VerrazzanoManagedLabelKey])
    68  
    69  	// Verify MultiClusterApplicationConfiguration got created on local cluster
    70  	mcAppConfig := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
    71  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfig.Name, Namespace: testMCAppConfig.Namespace}, mcAppConfig)
    72  	assert.NoError(err)
    73  	assert.Equal(mcAppConfig.Labels, mcAppConfigExpectedLabels, "mcappconfig labels did not match")
    74  	assert.Equal(testClusterName, mcAppConfig.Spec.Placement.Clusters[0].Name, "mcappconfig does not contain expected placement")
    75  }
    76  
    77  // TestCreateMCAppConfigNoOAMComponent tests the synchronization method for the following use case.
    78  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
    79  // WHEN the component referenced is a MultiClusterComponent
    80  // THEN ensure that the MultiClusterApplicationConfiguration is created but not the OAM component
    81  func TestCreateMCAppConfigNoOAMComponent(t *testing.T) {
    82  	assert := asserts.New(t)
    83  	log := zap.S().With("test")
    84  
    85  	// Test data
    86  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
    87  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
    88  
    89  	testMCComponent, err := getSampleMCComponent("testdata/mc-hello-component.yaml")
    90  	assert.NoError(err, "failed to read sample data for MultiCusterComponent")
    91  
    92  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testMCAppConfig).Build()
    93  
    94  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testMCComponent).Build()
    95  
    96  	// Make the request
    97  	s := &Syncer{
    98  		AdminClient:        adminClient,
    99  		LocalClient:        localClient,
   100  		Log:                log,
   101  		ManagedClusterName: testClusterName,
   102  		Context:            context.TODO(),
   103  	}
   104  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   105  
   106  	// Validate the results
   107  	assert.NoError(err)
   108  
   109  	// Verify the associated OAM component did not get created on local cluster since we are
   110  	// using a MultiClusterComponent instead of an OAM Component in the MultuClusterApplicationConfiguration
   111  	component := &oamv1alpha2.Component{}
   112  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCComponent.Name, Namespace: testMCComponent.Namespace}, component)
   113  	assert.True(errors.IsNotFound(err))
   114  
   115  	// Verify MultiClusterApplicationConfiguration got created on local cluster
   116  	mcAppConfig := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   117  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfig.Name, Namespace: testMCAppConfig.Namespace}, mcAppConfig)
   118  	assert.NoError(err)
   119  	assert.Equal(mcAppConfig.Labels, mcAppConfigExpectedLabels, "mcappconfig labels did not match")
   120  	assert.Equal(testClusterName, mcAppConfig.Spec.Placement.Clusters[0].Name, "mcappconfig does not contain expected placement")
   121  }
   122  
   123  // TestUpdateMCAppConfig tests the synchronization method for the following use case.
   124  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
   125  // WHEN the object exists
   126  // THEN ensure that the MultiClusterApplicationConfiguration is updated.
   127  func TestUpdateMCAppConfig(t *testing.T) {
   128  	assert := asserts.New(t)
   129  	log := zap.S().With("test")
   130  
   131  	// Test data
   132  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   133  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   134  
   135  	testMCAppConfigUpdate, err := getSampleMCAppConfig("testdata/multicluster-appconfig-update.yaml")
   136  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   137  
   138  	testComponent1, err := getSampleOamComponent("testdata/hello-component.yaml")
   139  	assert.NoError(err, "failed to read sample data for OAM Component")
   140  
   141  	testComponent2, err := getSampleOamComponent("testdata/goodbye-component.yaml")
   142  	assert.NoError(err, "failed to read sample data for OAM Component")
   143  
   144  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testMCAppConfigUpdate, &testComponent1, &testComponent2).Build()
   145  
   146  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testMCAppConfig, &testComponent1, &testComponent2).Build()
   147  
   148  	// Make the request
   149  	s := &Syncer{
   150  		AdminClient:        adminClient,
   151  		LocalClient:        localClient,
   152  		Log:                log,
   153  		ManagedClusterName: testClusterName,
   154  		Context:            context.TODO(),
   155  	}
   156  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   157  
   158  	// Validate the results
   159  	assert.NoError(err)
   160  
   161  	// Verify the MultiClusterApplicationConfiguration on the managed cluster is equal to the one on the admin cluster
   162  	mcAppConfig := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   163  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfig.Name, Namespace: testMCAppConfig.Namespace}, mcAppConfig)
   164  	assert.NoError(err)
   165  	assert.Equal(mcAppConfigExpectedLabelsAfterUpdate, mcAppConfig.Labels, "mcappconfig labels did not match")
   166  	assert.Equal("Hello application updated", mcAppConfig.Spec.Template.Metadata.Annotations["description"])
   167  	assert.Equal(2, len(mcAppConfig.Spec.Template.Spec.Components))
   168  	comp0 := mcAppConfig.Spec.Template.Spec.Components[0]
   169  	comp1 := mcAppConfig.Spec.Template.Spec.Components[1]
   170  	assert.Equal("hello-component", comp0.ComponentName)
   171  	assert.Equal("goodbye-component", comp1.ComponentName)
   172  
   173  	// Verify the associated OAM component got created on local cluster
   174  	component1 := &oamv1alpha2.Component{}
   175  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testComponent1.Name, Namespace: testComponent1.Namespace}, component1)
   176  	assert.NoError(err)
   177  	assert.Equal(s.ManagedClusterName, component1.Labels[managedClusterLabel])
   178  	assert.Equal(testMCAppConfig.Name, component1.Labels[mcAppConfigsLabel])
   179  
   180  	component2 := &oamv1alpha2.Component{}
   181  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testComponent2.Name, Namespace: testComponent2.Namespace}, component2)
   182  	assert.NoError(err)
   183  	assert.Equal(s.ManagedClusterName, component2.Labels[managedClusterLabel])
   184  	assert.Equal(testMCAppConfig.Name, component2.Labels[mcAppConfigsLabel])
   185  }
   186  
   187  // TestDeleteMCAppConfig tests the synchronization method for the following use case.
   188  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
   189  // WHEN the object exists on the local cluster but not on the admin cluster
   190  // THEN ensure that the MultiClusterApplicationConfiguration is deleted.
   191  func TestDeleteMCAppConfig(t *testing.T) {
   192  	assert := asserts.New(t)
   193  	log := zap.S().With("test")
   194  
   195  	// Test data
   196  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   197  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   198  
   199  	testMCAppConfigOrphan, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   200  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   201  	testMCAppConfigOrphan.Name = "orphaned-resource"
   202  
   203  	testComponent, err := getSampleOamComponent("testdata/hello-component.yaml")
   204  	assert.NoError(err, "failed to read sample data for OAM Component")
   205  
   206  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testMCAppConfig, &testComponent).Build()
   207  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testComponent, &testMCAppConfig, &testMCAppConfigOrphan).Build()
   208  
   209  	// Make the request
   210  	s := &Syncer{
   211  		AdminClient:        adminClient,
   212  		LocalClient:        localClient,
   213  		Log:                log,
   214  		ManagedClusterName: testClusterName,
   215  		Context:            context.TODO(),
   216  	}
   217  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   218  
   219  	// Validate the results
   220  	assert.NoError(err)
   221  
   222  	// Expect the orphaned MultiClusterApplicationConfiguration object to be deleted from the local cluster
   223  	appConfig := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   224  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfigOrphan.Name, Namespace: testMCAppConfigOrphan.Namespace}, appConfig)
   225  	assert.True(errors.IsNotFound(err))
   226  
   227  	// Delete the MultiClusterApplicationConfiguration from the admin cluster
   228  	err = s.AdminClient.Delete(s.Context, &testMCAppConfig)
   229  	assert.NoError(err)
   230  
   231  	// Synchronize again and check for cleanup on the local cluster
   232  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   233  	assert.NoError(err)
   234  
   235  	// Expect the MultiClusterApplicationConfiguration object to be deleted from the local cluster
   236  	appConfig2 := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   237  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfig.Name, Namespace: testMCAppConfig.Namespace}, appConfig2)
   238  	assert.True(errors.IsNotFound(err))
   239  
   240  	// Expect the OAM Component used by the application to be deleted from the local cluster
   241  	component := &oamv1alpha2.Component{}
   242  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testComponent.Name, Namespace: testComponent.Namespace}, component)
   243  	assert.True(errors.IsNotFound(err))
   244  }
   245  
   246  // TestDeleteMCAppConfigNoOAMComponent tests the synchronization method for the following use case.
   247  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
   248  // WHEN a MultiClusterApplicationConfiguration object is deleted from the admin cluster that references a
   249  // MultiClusterComponent object.
   250  // THEN ensure that the MultiClusterApplicationConfiguration is deleted and OAM component object is not deleted
   251  func TestDeleteMCAppConfigNoOAMComponent(t *testing.T) {
   252  	assert := asserts.New(t)
   253  	log := zap.S().With("test")
   254  
   255  	// Test data
   256  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   257  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   258  
   259  	testOAMComponent, err := getSampleOamComponent("testdata/hello-component.yaml")
   260  	assert.NoError(err, "failed to read sample data for Component")
   261  
   262  	testMCComponent, err := getSampleMCComponent("testdata/mc-hello-component.yaml")
   263  	assert.NoError(err, "failed to read sample data for MultiClusterComponent")
   264  
   265  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testMCAppConfig).Build()
   266  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testOAMComponent, &testMCComponent).Build()
   267  
   268  	// Make the request
   269  	s := &Syncer{
   270  		AdminClient:        adminClient,
   271  		LocalClient:        localClient,
   272  		Log:                log,
   273  		ManagedClusterName: testClusterName,
   274  		Context:            context.TODO(),
   275  	}
   276  
   277  	// Set cluster label on OAM Component
   278  	testOAMComponent.Labels[managedClusterLabel] = "managed1"
   279  	err = s.LocalClient.Update(s.Context, &testOAMComponent)
   280  	assert.NoError(err)
   281  
   282  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   283  	assert.NoError(err)
   284  
   285  	// Verify OAM Component exists on local cluster
   286  	component := &oamv1alpha2.Component{}
   287  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testOAMComponent.Name, Namespace: testOAMComponent.Namespace}, component)
   288  	assert.NoError(err)
   289  
   290  	// Verify MultiClusterApplicationConfiguration got created on local cluster
   291  	mcAppConfig := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   292  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfig.Name, Namespace: testMCAppConfig.Namespace}, mcAppConfig)
   293  	assert.NoError(err)
   294  
   295  	// Delete the MultiClusterApplicationConfiguration from the admin cluster
   296  	err = s.AdminClient.Delete(s.Context, &testMCAppConfig)
   297  	assert.NoError(err)
   298  
   299  	// Synchronize again and check for cleanup on the local cluster
   300  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   301  	assert.NoError(err)
   302  
   303  	// Expect the MultiClusterApplicationConfiguration object to be deleted from the local cluster
   304  	mcAppConfig = &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   305  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfig.Name, Namespace: testMCAppConfig.Namespace}, mcAppConfig)
   306  	assert.True(errors.IsNotFound(err))
   307  
   308  	// Expect the OAM Component used by the application to NOT be deleted from the local cluster
   309  	component = &oamv1alpha2.Component{}
   310  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testOAMComponent.Name, Namespace: testOAMComponent.Namespace}, component)
   311  	assert.NoError(err)
   312  }
   313  
   314  // TestDeleteMCAppConfigShared tests the synchronization method for the following use case.
   315  // GIVEN a request to sync two MultiClusterApplicationConfiguration objects that shared an OAM Component
   316  // WHEN the object exists on the local cluster but not on the admin cluster
   317  // THEN ensure that when MultiClusterApplicationConfiguration is deleted, the shared OAM component is not
   318  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
   319  // WHEN no remaining MultiClusterApplicationConfiguration exist on the admin cluster
   320  // THEN ensure that when MultiClusterApplicationConfiguration is deleted, the OAM component that is no longer shared is deleted
   321  func TestDeleteMCAppConfigShared(t *testing.T) {
   322  	assert := asserts.New(t)
   323  	log := zap.S().With("test")
   324  
   325  	// Test data
   326  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   327  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   328  
   329  	testMCAppConfig2, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   330  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   331  	testMCAppConfig2.Name = testMCAppConfig.Name + "2"
   332  
   333  	testComponent, err := getSampleOamComponent("testdata/hello-component.yaml")
   334  	assert.NoError(err, "failed to read sample data for OAM Component")
   335  
   336  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testMCAppConfig, &testComponent).Build()
   337  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testComponent, &testMCAppConfig, &testMCAppConfig2).Build()
   338  
   339  	// Make the request
   340  	s := &Syncer{
   341  		AdminClient:        adminClient,
   342  		LocalClient:        localClient,
   343  		Log:                log,
   344  		ManagedClusterName: testClusterName,
   345  		Context:            context.TODO(),
   346  	}
   347  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   348  
   349  	// Validate the results
   350  	assert.NoError(err)
   351  
   352  	// Expect the MultiClusterApplicationConfiguration object to be deleted from the local cluster
   353  	appConfig := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   354  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfig2.Name, Namespace: testMCAppConfig2.Namespace}, appConfig)
   355  	assert.True(errors.IsNotFound(err))
   356  
   357  	// Expect the OAM Component shared by the applications to still exist on the local cluster
   358  	component := &oamv1alpha2.Component{}
   359  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testComponent.Name, Namespace: testComponent.Namespace}, component)
   360  	assert.NoError(err)
   361  	assert.Equal(testMCAppConfig.Name, component.Labels[mcAppConfigsLabel])
   362  
   363  	// Delete the remaining MultiClusterApplicationConfiguration in the Admin cluster and verify cleanup on the local cluster
   364  	err = s.AdminClient.Delete(s.Context, &testMCAppConfig)
   365  	assert.NoError(err)
   366  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   367  	assert.NoError(err)
   368  
   369  	// Expect the MultiClusterApplicationConfiguration object to be deleted from the local cluster
   370  	appConfig2 := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   371  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testMCAppConfig.Name, Namespace: testMCAppConfig.Namespace}, appConfig2)
   372  	assert.True(errors.IsNotFound(err))
   373  
   374  	// Expect the OAM Component that used to be shared by the applications to be deleted from the local cluster
   375  	component2 := &oamv1alpha2.Component{}
   376  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testComponent.Name, Namespace: testComponent.Namespace}, component2)
   377  	assert.True(errors.IsNotFound(err))
   378  }
   379  
   380  // TestDeleteOrphanedComponents tests the synchronization method for the following use case.
   381  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
   382  // WHEN an OAM component exists on a cluster that is no longer associated with any MultiClusterApplicationConfiguration
   383  // THEN ensure that the orphaned OAM component gets deleted
   384  func TestDeleteOrphanedComponents(t *testing.T) {
   385  	assert := asserts.New(t)
   386  	log := zap.S().With("test")
   387  
   388  	// Test data
   389  
   390  	// Add labels that would have been applied when the OAM component was synced to the local system
   391  	testComponent1, err := getSampleOamComponent("testdata/hello-component.yaml")
   392  	assert.NoError(err, "failed to read sample data for OAM Component")
   393  	testComponent1.Labels[managedClusterLabel] = testClusterName
   394  	testComponent1.Labels[mcAppConfigsLabel] = ""
   395  
   396  	// Do not add any Verrazzano labels to this component
   397  	testComponent2, err := getSampleOamComponent("testdata/goodbye-component.yaml")
   398  	assert.NoError(err, "failed to read sample data for OAM Component")
   399  
   400  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).Build()
   401  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&testComponent1, &testComponent2).Build()
   402  
   403  	// Make the request
   404  	s := &Syncer{
   405  		AdminClient:        adminClient,
   406  		LocalClient:        localClient,
   407  		Log:                log,
   408  		ManagedClusterName: testClusterName,
   409  		Context:            context.TODO(),
   410  	}
   411  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   412  
   413  	// Validate the results
   414  	assert.NoError(err)
   415  
   416  	// Expect the orphaned OAM Component to be deleted from the local cluster
   417  	component1 := &oamv1alpha2.Component{}
   418  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testComponent1.Name, Namespace: testComponent1.Namespace}, component1)
   419  	assert.True(errors.IsNotFound(err))
   420  
   421  	// Expect the OAM component that was not synced to still exist
   422  	component2 := &oamv1alpha2.Component{}
   423  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testComponent2.Name, Namespace: testComponent2.Namespace}, component2)
   424  	assert.NoError(err)
   425  }
   426  
   427  // TestMCAppConfigPlacement tests the synchronization method for the following use case.
   428  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
   429  // WHEN an object exists that is not targeted for the cluster
   430  // THEN ensure that the MultiClusterApplicationConfiguration is not created or updated
   431  func TestMCAppConfigPlacement(t *testing.T) {
   432  	assert := asserts.New(t)
   433  	log := zap.S().With("test")
   434  
   435  	// Test data
   436  	adminMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   437  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   438  	adminMCAppConfig.Spec.Placement.Clusters[0].Name = "not-my-cluster"
   439  
   440  	localMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   441  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   442  
   443  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&adminMCAppConfig).Build()
   444  
   445  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(&localMCAppConfig).Build()
   446  
   447  	// Make the request
   448  	s := &Syncer{
   449  		AdminClient:        adminClient,
   450  		LocalClient:        localClient,
   451  		Log:                log,
   452  		ManagedClusterName: testClusterName,
   453  		Context:            context.TODO(),
   454  	}
   455  	err = s.syncMCApplicationConfigurationObjects(testMCAppConfigNamespace)
   456  
   457  	// Verify the local MultiClusterApplicationConiguration was deleted
   458  	assert.NoError(err)
   459  	delAppConfig := &clustersv1alpha1.MultiClusterApplicationConfiguration{}
   460  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: localMCAppConfig.Name, Namespace: localMCAppConfig.Namespace}, delAppConfig)
   461  	assert.True(errors.IsNotFound(err))
   462  }
   463  
   464  // TestSyncComponentList tests the synchronization method for the following use case.
   465  // GIVEN a request to sync MultiClusterApplicationConfiguration objects
   466  // WHEN it contains a list of OAM Components
   467  // THEN ensure that the embedded OAM Components are created or updated
   468  func TestSyncComponentList(t *testing.T) {
   469  	appName := "test"
   470  	appNamespace := "test-ns"
   471  	compName1 := "test-comp-1"
   472  	compName2 := "test-comp-2"
   473  	param1 := "parameter-1"
   474  	param2 := "parameter-2"
   475  	testLabel := "test-label"
   476  	testAnnot := "test-annotation"
   477  
   478  	assert := asserts.New(t)
   479  	log := zap.S().With("test")
   480  
   481  	// Create a fake client for the admin cluster
   482  	adminClient := fake.NewClientBuilder().WithScheme(newScheme()).WithObjects(
   483  		&oamv1alpha2.Component{
   484  			ObjectMeta: metav1.ObjectMeta{
   485  				Name:        compName1,
   486  				Namespace:   appNamespace,
   487  				Labels:      map[string]string{"test": testLabel},
   488  				Annotations: map[string]string{"test": testAnnot}},
   489  			Spec: oamv1alpha2.ComponentSpec{
   490  				Parameters: []oamv1alpha2.ComponentParameter{
   491  					{
   492  						Name: param1,
   493  					},
   494  				},
   495  			},
   496  		},
   497  		&oamv1alpha2.Component{
   498  			ObjectMeta: metav1.ObjectMeta{
   499  				Name:        compName2,
   500  				Namespace:   appNamespace,
   501  				Labels:      map[string]string{"test": testLabel},
   502  				Annotations: map[string]string{"test": testAnnot}},
   503  			Spec: oamv1alpha2.ComponentSpec{
   504  				Parameters: []oamv1alpha2.ComponentParameter{
   505  					{
   506  						Name: param2,
   507  					},
   508  				},
   509  			},
   510  		},
   511  	).Build()
   512  
   513  	// Create a fake client for the local cluster
   514  	localClient := fake.NewClientBuilder().WithScheme(newScheme()).Build()
   515  
   516  	// MultiClusterApplicationConfiguration test data
   517  	mcAppConfig := clustersv1alpha1.MultiClusterApplicationConfiguration{
   518  		ObjectMeta: metav1.ObjectMeta{Name: appName, Namespace: appNamespace},
   519  		Spec: clustersv1alpha1.MultiClusterApplicationConfigurationSpec{
   520  			Template: clustersv1alpha1.ApplicationConfigurationTemplate{
   521  				Spec: oamv1alpha2.ApplicationConfigurationSpec{
   522  					Components: []oamv1alpha2.ApplicationConfigurationComponent{
   523  						{
   524  							ComponentName: compName1,
   525  						},
   526  						{
   527  							ComponentName: compName2,
   528  						},
   529  					},
   530  				},
   531  			},
   532  		},
   533  	}
   534  
   535  	// Make the request
   536  	s := &Syncer{
   537  		AdminClient:        adminClient,
   538  		LocalClient:        localClient,
   539  		Log:                log,
   540  		ManagedClusterName: testClusterName,
   541  		Context:            context.TODO(),
   542  	}
   543  	err := s.syncComponentList(mcAppConfig)
   544  	assert.NoError(err)
   545  
   546  	// Verify the components were created locally
   547  	component1 := &oamv1alpha2.Component{}
   548  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: compName1, Namespace: appNamespace}, component1)
   549  	assert.NoError(err)
   550  	assert.Equal(param1, component1.Spec.Parameters[0].Name)
   551  	assert.Equal(testLabel, component1.ObjectMeta.Labels["test"])
   552  	assert.Equal(testAnnot, component1.ObjectMeta.Annotations["test"])
   553  
   554  	component2 := &oamv1alpha2.Component{}
   555  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: compName2, Namespace: appNamespace}, component2)
   556  	assert.NoError(err)
   557  	assert.Equal(param2, component2.Spec.Parameters[0].Name)
   558  	assert.Equal(testLabel, component2.ObjectMeta.Labels["test"])
   559  	assert.Equal(testAnnot, component2.ObjectMeta.Annotations["test"])
   560  }
   561  
   562  // getSampleMCAppConfig creates and returns a sample MultiClusterApplicationConfiguration used in tests
   563  func getSampleMCAppConfig(filePath string) (clustersv1alpha1.MultiClusterApplicationConfiguration, error) {
   564  	mcAppConfig := clustersv1alpha1.MultiClusterApplicationConfiguration{}
   565  	sampleAppConfigFile, err := filepath.Abs(filePath)
   566  	if err != nil {
   567  		return mcAppConfig, err
   568  	}
   569  
   570  	rawResource, err := clusterstest.ReadYaml2Json(sampleAppConfigFile)
   571  	if err != nil {
   572  		return mcAppConfig, err
   573  	}
   574  
   575  	err = json.Unmarshal(rawResource, &mcAppConfig)
   576  	return mcAppConfig, err
   577  }
   578  
   579  // getSampleOamComponent creates and returns a sample OAM Component
   580  func getSampleOamComponent(filePath string) (oamv1alpha2.Component, error) {
   581  	component := oamv1alpha2.Component{}
   582  	sampleComponentFile, err := filepath.Abs(filePath)
   583  	if err != nil {
   584  		return component, err
   585  	}
   586  
   587  	rawResource, err := clusterstest.ReadYaml2Json(sampleComponentFile)
   588  	if err != nil {
   589  		return component, err
   590  	}
   591  
   592  	err = json.Unmarshal(rawResource, &component)
   593  	return component, err
   594  }
   595  
   596  func newScheme() *runtime.Scheme {
   597  	scheme := runtime.NewScheme()
   598  	oamv1alpha2.SchemeBuilder.AddToScheme(scheme)
   599  	clustersv1alpha1.AddToScheme(scheme)
   600  	return scheme
   601  }