@@ -21,6 +21,7 @@ import (
2121	"testing" 
2222	"time" 
2323
24+ 	"github.com/aws/aws-sdk-go-v2/aws" 
2425	"github.com/aws/smithy-go" 
2526	"github.com/stretchr/testify/assert" 
2627	"github.com/stretchr/testify/mock" 
@@ -29,10 +30,17 @@ import (
2930	corev1 "k8s.io/api/core/v1" 
3031	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 
3132	k8sobj "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 
33+ 	"k8s.io/apimachinery/pkg/runtime/schema" 
3234	k8srtschema "k8s.io/apimachinery/pkg/runtime/schema" 
35+ 	"k8s.io/apimachinery/pkg/types" 
36+ 	k8sfake "k8s.io/client-go/kubernetes/fake" 
37+ 	ctrlrt "sigs.k8s.io/controller-runtime" 
3338	ctrlrtzap "sigs.k8s.io/controller-runtime/pkg/log/zap" 
3439
3540	ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" 
41+ 	k8srtschemamocks "github.com/aws-controllers-k8s/runtime/mocks/apimachinery/pkg/runtime/schema" 
42+ 	ctrlrtclientmock "github.com/aws-controllers-k8s/runtime/mocks/controller-runtime/pkg/client" 
43+ 	ackmocks "github.com/aws-controllers-k8s/runtime/mocks/pkg/types" 
3644	ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare" 
3745	ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" 
3846	ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" 
@@ -42,10 +50,6 @@ import (
4250	"github.com/aws-controllers-k8s/runtime/pkg/requeue" 
4351	ackrtcache "github.com/aws-controllers-k8s/runtime/pkg/runtime/cache" 
4452	acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" 
45- 
46- 	k8srtschemamocks "github.com/aws-controllers-k8s/runtime/mocks/apimachinery/pkg/runtime/schema" 
47- 	ctrlrtclientmock "github.com/aws-controllers-k8s/runtime/mocks/controller-runtime/pkg/client" 
48- 	ackmocks "github.com/aws-controllers-k8s/runtime/mocks/pkg/types" 
4953)
5054
5155// isWithoutCancelContext checks if the context is a WithoutCancel context 
@@ -503,7 +507,7 @@ func TestReconcilerAdoptOrCreateResource_Adopt(t *testing.T) {
503507	latest , latestRTObj , latestMetaObj  :=  resourceMocks ()
504508	latest .On ("Identifiers" ).Return (ids )
505509	latest .On ("Conditions" ).Return ([]* ackv1alpha1.Condition {})
506- 		 latest .On (
510+ 	latest .On (
507511		"ReplaceConditions" ,
508512		mock .AnythingOfType ("[]*v1alpha1.Condition" ),
509513	).Return ().Run (func (args  mock.Arguments ) {
@@ -1746,3 +1750,146 @@ func TestReconcilerUpdate_EnsureControllerTagsError(t *testing.T) {
17461750	rm .AssertNotCalled (t , "LateInitialize" , ctx , latest )
17471751	rm .AssertCalled (t , "EnsureTags" , ctx , desired , scmd )
17481752}
1753+ 
1754+ func  TestReconcile_AccountDrifted (t  * testing.T ) {
1755+ 	require  :=  require .New (t )
1756+ 
1757+ 	ctx  :=  context .TODO ()
1758+ 	req  :=  ctrlrt.Request {
1759+ 		NamespacedName : types.NamespacedName {
1760+ 			Namespace : "production" ,
1761+ 			Name :      "mybook" ,
1762+ 		},
1763+ 	}
1764+ 
1765+ 	// Create resource with existing account 
1766+ 	existingAccount  :=  ackv1alpha1 .AWSAccountID ("111111111111" )
1767+ 
1768+ 	desired , _ , metaObj  :=  resourceMocks ()
1769+ 	metaObj .SetNamespace ("production" )
1770+ 
1771+ 	ids  :=  & ackmocks.AWSResourceIdentifiers {}
1772+ 	ids .On ("Region" ).Return (nil )
1773+ 	ids .On ("OwnerAccountID" ).Return (& existingAccount )
1774+ 	desired .On ("Identifiers" ).Return (ids )
1775+ 	desired .On ("Conditions" ).Return ([]* ackv1alpha1.Condition {})
1776+ 	desired .On (
1777+ 		"ReplaceConditions" ,
1778+ 		mock .AnythingOfType ("[]*v1alpha1.Condition" ),
1779+ 	).Return ()
1780+ 	desired .On ("IsBeingDeleted" ).Return (false )
1781+ 
1782+ 	// Setup resource descriptor 
1783+ 	rd  :=  & ackmocks.AWSResourceDescriptor {}
1784+ 	rd .On ("GroupVersionKind" ).Return (schema.GroupVersionKind {
1785+ 		Group :   "test.services.k8s.aws" ,
1786+ 		Kind :    "Book" ,
1787+ 		Version : "v1alpha1" ,
1788+ 	})
1789+ 	rd .On ("EmptyRuntimeObject" ).Return (& fakeBook {})
1790+ 	rd .On ("ResourceFromRuntimeObject" , mock .Anything ).Return (desired )
1791+ 
1792+ 	// Setup service controller 
1793+ 	sc  :=  & ackmocks.ServiceController {}
1794+ 	sc .On ("GetMetadata" ).Return (acktypes.ServiceControllerMetadata {})
1795+ 	sc .On ("NewAWSConfig" ,
1796+ 		mock .Anything ,
1797+ 		mock .AnythingOfType ("v1alpha1.AWSRegion" ),
1798+ 		mock .Anything ,
1799+ 		mock .AnythingOfType ("v1alpha1.AWSResourceName" ),
1800+ 		mock .AnythingOfType ("schema.GroupVersionKind" ),
1801+ 	).Return (aws.Config {}, nil )
1802+ 
1803+ 	// Get fakeLogger 
1804+ 	zapOptions  :=  ctrlrtzap.Options {
1805+ 		Development : true ,
1806+ 		Level :       zapcore .InfoLevel ,
1807+ 	}
1808+ 	fakeLogger  :=  ctrlrtzap .New (ctrlrtzap .UseFlagOptions (& zapOptions ))
1809+ 
1810+ 	// Create fake k8s client with namespace that has owner account annotation 
1811+ 	k8sClient  :=  k8sfake .NewSimpleClientset ()
1812+ 
1813+ 	// Create namespace with owner account annotation 
1814+ 	namespace  :=  & corev1.Namespace {
1815+ 		ObjectMeta : metav1.ObjectMeta {
1816+ 			Name : "production" ,
1817+ 			Annotations : map [string ]string {
1818+ 				ackv1alpha1 .AnnotationOwnerAccountID : "222222222222" ,
1819+ 			},
1820+ 		},
1821+ 	}
1822+ 	k8sClient .CoreV1 ().Namespaces ().Create (context .Background (), namespace , metav1.CreateOptions {})
1823+ 
1824+ 	// Create CARM configmap 
1825+ 	configMap  :=  & corev1.ConfigMap {
1826+ 		ObjectMeta : metav1.ObjectMeta {
1827+ 			Name :      ackrtcache .ACKRoleAccountMap ,
1828+ 			Namespace : "ack-system" ,
1829+ 		},
1830+ 		Data : map [string ]string {
1831+ 			"222222222222" : "arn:aws:iam::222222222222:role/ACKRole" ,
1832+ 		},
1833+ 	}
1834+ 	k8sClient .CoreV1 ().ConfigMaps ("ack-system" ).Create (context .Background (), configMap , metav1.CreateOptions {})
1835+ 
1836+ 	// Create caches with the k8s client 
1837+ 	caches  :=  ackrtcache .New (fakeLogger , ackrtcache.Config {}, featuregate.FeatureGates {})
1838+ 
1839+ 	// Run the caches 
1840+ 	stopCh  :=  make (chan  struct {})
1841+ 	defer  close (stopCh )
1842+ 	caches .Run (k8sClient )
1843+ 
1844+ 	// Wait for caches to sync 
1845+ 	time .Sleep (100  *  time .Millisecond )
1846+ 
1847+ 	kc  :=  & ctrlrtclientmock.Client {}
1848+ 	statusWriter  :=  & ctrlrtclientmock.SubResourceWriter {}
1849+ 	kc .On ("Status" ).Return (statusWriter )
1850+ 	statusWriter .On ("Patch" , mock .Anything , mock .Anything , mock .Anything ).Return (nil )
1851+ 
1852+ 	rm  :=  & ackmocks.AWSResourceManager {}
1853+ 	rmf  :=  & ackmocks.AWSResourceManagerFactory {}
1854+ 	rmf .On ("ResourceDescriptor" ).Return (rd )
1855+ 	rmf .On ("ManagerFor" ,
1856+ 		mock .Anything ,
1857+ 		mock .Anything ,
1858+ 		mock .Anything ,
1859+ 		mock .Anything ,
1860+ 		mock .Anything ,
1861+ 		mock .AnythingOfType ("v1alpha1.AWSAccountID" ),
1862+ 		mock .AnythingOfType ("v1alpha1.AWSRegion" ),
1863+ 		mock .AnythingOfType ("v1alpha1.AWSResourceName" ),
1864+ 	).Return (rm , nil )
1865+ 	rm .On ("ResolveReferences" , mock .Anything , mock .Anything , mock .Anything ).Return (
1866+ 		desired , false , nil ,
1867+ 	)
1868+ 	rm .On ("EnsureTags" , mock .Anything , mock .Anything , mock .Anything ).Return (nil )
1869+ 
1870+ 	// Create reconciler with namespace cache 
1871+ 	r  :=  & resourceReconciler {
1872+ 		reconciler : reconciler {
1873+ 			kc :      kc ,
1874+ 			sc :      sc ,
1875+ 			log :     fakeLogger ,
1876+ 			cfg :     ackcfg.Config {AccountID : "333333333333" },
1877+ 			cache :   caches ,
1878+ 			metrics : ackmetrics .NewMetrics ("test" ),
1879+ 		},
1880+ 		rmf : rmf ,
1881+ 		rd :  rd ,
1882+ 	}
1883+ 
1884+ 	apiReader  :=  & ctrlrtclientmock.Reader {}
1885+ 	apiReader .On ("Get" , ctx , req .NamespacedName , mock .AnythingOfType ("*runtime.fakeBook" )).Return (nil )
1886+ 	r .apiReader  =  apiReader 
1887+ 
1888+ 	// Call Reconcile 
1889+ 	_ , err  :=  r .Reconcile (ctx , req )
1890+ 
1891+ 	// Should get terminal error for account drift 
1892+ 	require .NotNil (err )
1893+ 	assert .Contains (t , err .Error (), "Resource already exists in account 111111111111" )
1894+ 	assert .Contains (t , err .Error (), "but the role used for reconciliation is in account 222222222222" )
1895+ }
0 commit comments