Skip to content

Commit 22833ca

Browse files
committed
add
1 parent 7624d0d commit 22833ca

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

pkg/runtime/reconciler.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,39 @@ func (r *resourceReconciler) Reconcile(ctx context.Context, req ctrlrt.Request)
262262
region := r.getRegion(desired)
263263
endpointURL := r.getEndpointURL(desired)
264264
gvk := r.rd.GroupVersionKind()
265+
266+
// If the user has specified a region that is different from the
267+
// region the resource currently exists in, we need to fail the
268+
// reconciliation with a terminal error.
269+
if regionDrifted(desired, region) {
270+
msg := fmt.Sprintf(
271+
"Resource already exists in region %s, but the desired state specifies region %s. ",
272+
*desired.Identifiers().Region(), region,
273+
)
274+
rlog.Info(
275+
msg,
276+
"current_region", region,
277+
"desired_region", desired.Identifiers().Region(),
278+
)
279+
return ctrlrt.Result{}, ackerr.NewTerminalError(errors.New(msg))
280+
}
281+
282+
// Similarly, if the user has specified an account ID that is different
283+
// from the account ID the resource currently exists in, we need to
284+
// fail the reconciliation with a terminal error.
285+
if accountDrifted(desired, acctID) {
286+
msg := fmt.Sprintf(
287+
"Resource already exists in account %s, but the role used for reconciliation is in account %s. ",
288+
*desired.Identifiers().OwnerAccountID(), acctID,
289+
)
290+
rlog.Info(
291+
msg,
292+
"current_account", acctID,
293+
"desired_account", desired.Identifiers().OwnerAccountID(),
294+
)
295+
return ctrlrt.Result{}, ackerr.NewTerminalError(errors.New(msg))
296+
}
297+
265298
// The config pivot to the roleARN will happen if it is not empty.
266299
// in the NewResourceManager
267300
clientConfig, err := r.sc.NewAWSConfig(ctx, region, &endpointURL, roleARN, gvk)
@@ -285,6 +318,20 @@ func (r *resourceReconciler) Reconcile(ctx context.Context, req ctrlrt.Request)
285318
return r.HandleReconcileError(ctx, desired, latest, err)
286319
}
287320

321+
func regionDrifted(desired acktypes.AWSResource, targetRegion ackv1alpha1.AWSRegion) bool {
322+
if desired.Identifiers().Region() == nil {
323+
return false
324+
}
325+
return *desired.Identifiers().Region() != targetRegion
326+
}
327+
328+
func accountDrifted(desired acktypes.AWSResource, targetAccountID ackv1alpha1.AWSAccountID) bool {
329+
if desired.Identifiers().OwnerAccountID() == nil {
330+
return false
331+
}
332+
return *desired.Identifiers().OwnerAccountID() != targetAccountID
333+
}
334+
288335
func (r *resourceReconciler) handleCacheError(
289336
ctx context.Context,
290337
err error,

pkg/runtime/reconciler_test.go

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3131
k8sobj "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3232
k8srtschema "k8s.io/apimachinery/pkg/runtime/schema"
33+
ctrlrt "sigs.k8s.io/controller-runtime"
3334
ctrlrtzap "sigs.k8s.io/controller-runtime/pkg/log/zap"
3435

3536
ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1"
@@ -503,7 +504,7 @@ func TestReconcilerAdoptOrCreateResource_Adopt(t *testing.T) {
503504
latest, latestRTObj, latestMetaObj := resourceMocks()
504505
latest.On("Identifiers").Return(ids)
505506
latest.On("Conditions").Return([]*ackv1alpha1.Condition{})
506-
latest.On(
507+
latest.On(
507508
"ReplaceConditions",
508509
mock.AnythingOfType("[]*v1alpha1.Condition"),
509510
).Return().Run(func(args mock.Arguments) {
@@ -1746,3 +1747,71 @@ func TestReconcilerUpdate_EnsureControllerTagsError(t *testing.T) {
17461747
rm.AssertNotCalled(t, "LateInitialize", ctx, latest)
17471748
rm.AssertCalled(t, "EnsureTags", ctx, desired, scmd)
17481749
}
1750+
1751+
func TestReconciler_RegionDrifted_Annotation(t *testing.T) {
1752+
require := require.New(t)
1753+
ctx := context.TODO()
1754+
1755+
// Resource has region "us-east-1" in Identifiers, but annotation says "us-west-2"
1756+
desired, _, metaObj := resourceMocks()
1757+
ids := &ackmocks.AWSResourceIdentifiers{}
1758+
region := ackv1alpha1.AWSRegion("us-east-1")
1759+
ids.On("Region").Return(&region)
1760+
desired.On("Identifiers").Return(ids)
1761+
metaObj.SetNamespace("default")
1762+
metaObj.SetAnnotations(map[string]string{
1763+
ackv1alpha1.AnnotationRegion: "us-west-2",
1764+
})
1765+
1766+
rmf, rd := managedResourceManagerFactoryMocks(desired, desired)
1767+
r, _, _ := reconcilerMocks(rmf)
1768+
rd.On("GroupVersionKind").Return(schema.GroupVersionKind{
1769+
Group: "bookstore.services.k8s.aws",
1770+
Kind: "Book",
1771+
Version: "v1alpha1",
1772+
})
1773+
rd.On("EmptyRuntimeObject").Return(&fakeBook{})
1774+
rd.On("IsManaged", desired).Return(true)
1775+
1776+
// This will trigger regionDrifted via getRegion logic
1777+
result, err := r.Reconcile(ctx, ctrlrt.Request{Namespace: "default", Name: "mybook"})
1778+
require.Error(err)
1779+
require.Contains(err.Error(), "region")
1780+
_ = result
1781+
}
1782+
1783+
func TestReconciler_AccountDrifted_NamespaceCache(t *testing.T) {
1784+
require := require.New(t)
1785+
ctx := context.TODO()
1786+
1787+
// Resource has account "111111111111" in Identifiers, but namespace cache says "222222222222"
1788+
desired, _, metaObj := resourceMocks()
1789+
ids := &ackmocks.AWSResourceIdentifiers{}
1790+
accountID := ackv1alpha1.AWSAccountID("111111111111")
1791+
ids.On("OwnerAccountID").Return(&accountID)
1792+
desired.On("Identifiers").Return(ids)
1793+
metaObj.SetNamespace("test-ns")
1794+
1795+
rmf, rd := managedResourceManagerFactoryMocks(desired, desired)
1796+
r, _, _ := reconcilerMocks(rmf)
1797+
rd.On("GroupVersionKind").Return(schema.GroupVersionKind{
1798+
Group: "bookstore.services.k8s.aws",
1799+
Kind: "Book",
1800+
Version: "v1alpha1",
1801+
})
1802+
rd.On("EmptyRuntimeObject").Return(&fakeBook{})
1803+
rd.On("IsManaged", desired).Return(true)
1804+
1805+
// Inject fake account ID into the reconciler's cache for the namespace
1806+
if r2, ok := r.(*ackrt.ResourceReconciler); ok {
1807+
if r2.Cache.Namespaces == nil {
1808+
r2.Cache.Namespaces = &ackrtcache.NamespacesCache{}
1809+
}
1810+
r2.Cache.Namespaces.SetOwnerAccountID("test-ns", "222222222222")
1811+
}
1812+
1813+
result, err := r.Reconcile(ctx, ctrlrt.Request{Namespace: "test-ns", Name: "mybook"})
1814+
require.Error(err)
1815+
require.Contains(err.Error(), "account")
1816+
_ = result
1817+
}

0 commit comments

Comments
 (0)