Skip to content

Commit 4d78380

Browse files
committed
Change force-adoption annotation to adoption-policy
This change will allow us to support an `adopt-or-create` policy in the future, where the controller will create the resource when the resource does not exist and can't be adopted. Before we support `adopt-or-create` we need a solution in code-gen where we change how we handle the `PopulateResourceFromAnnotation` which currently only allows the population of fields that are required for a readOne operation, and they happen to be all scalar fields (besides ARN, but we have a way of handling that), but the required fields for create would need to be sometimes structs, and this would require users to provide values in form of maps eg. Creating an EKS cluster requires a ResourceVPCConfig, which is a struct that contains subnetIDs etc. We can have a couple of ways to address this. 1. Accept these values in the spec, and return terminal error when we attempt a create and the create required fields are not provided 2. Accept these values in the `adoption-fields` annotation. This would need a code-gen change to allow reading from structs and assigning fields. but it would also make the annotation easy to make mistakes with when using yaml
1 parent ec0734c commit 4d78380

File tree

5 files changed

+66
-65
lines changed

5 files changed

+66
-65
lines changed

apis/core/v1alpha1/annotations.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,12 @@ const (
8181
// the resource is read-only and should not be created/patched/deleted by the
8282
// ACK service controller.
8383
AnnotationReadOnly = AnnotationPrefix + "read-only"
84-
// AnnotationForceAdoption is an annotation whose value is the identifier for whether
85-
// we will force adoption or not. If this annotation is set to true on a CR, that
86-
// means the user is indicating to the ACK service controller that it should
87-
// force adoption of the resource.
88-
AnnotationForceAdoption = AnnotationPrefix + "force-adoption"
84+
// AnnotationAdoptionPolicy is an annotation whose value is the identifier for whether
85+
// we will attempt adoption only (value = adopt-only) or attempt a create if resource
86+
// is not found (value adopt-or-create).
87+
//
88+
// NOTE (michaelhtm): Currently create-or-adopt is not supported
89+
AnnotationAdoptionPolicy = AnnotationPrefix + "adoption-policy"
8990
// AnnotationAdoptionFields is an annotation whose value contains a json-like
9091
// format of the requied fields to do a ReadOne when attempting to force-adopt
9192
// a Resource

pkg/featuregate/features.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ package featuregate
1919
import "fmt"
2020

2121
const (
22-
// AdoptResources is a feature gate for enabling forced adoption of resources
22+
// ResourceAdoption is a feature gate for enabling forced adoption of resources
2323
// by annotation
24-
AdoptResources = "AdoptResources"
24+
ResourceAdoption = "ResourceAdoption"
2525

2626
// ReadOnlyResources is a feature gate for enabling ReadOnly resources annotation.
2727
ReadOnlyResources = "ReadOnlyResources"
@@ -36,7 +36,7 @@ const (
3636
// defaultACKFeatureGates is a map of feature names to Feature structs
3737
// representing the default feature gates for ACK controllers.
3838
var defaultACKFeatureGates = FeatureGates{
39-
AdoptResources: {Stage: Alpha, Enabled: false},
39+
ResourceAdoption: {Stage: Alpha, Enabled: false},
4040
ReadOnlyResources: {Stage: Alpha, Enabled: false},
4141
TeamLevelCARM: {Stage: Alpha, Enabled: false},
4242
ServiceLevelCARM: {Stage: Alpha, Enabled: false},

pkg/runtime/reconciler.go

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ const (
5757
// resource if the CARM cache is not synced yet, or if the roleARN is not
5858
// available.
5959
roleARNNotAvailableRequeueDelay = 15 * time.Second
60+
// adoptOrCreate is an annotation field that decides whether to create the
61+
// resource if it doesn't exist, or adopt the resource if it exists.
62+
// value comes from getAdoptionPolicy
63+
// adoptOrCreate = "adopt-or-create"
6064
)
6165

6266
// reconciler describes a generic reconciler within ACK.
@@ -307,29 +311,23 @@ func (r *resourceReconciler) handleAdoption(
307311
// If the resource is being adopted by force, we need to access
308312
// the required field passed by annotation and attempt a read.
309313

310-
// continueReconcile will decide whether or not to continue the
311-
// reconciliation. The cases where this would happen is when the
312-
// resource is already adopted, or if we want to create the resource
313-
// if user wants to create it when not found
314-
if !NeedAdoption(desired) {
315-
return desired, nil
316-
}
317-
318314
rlog.Info("Adopting Resource")
319315
extractedFields, err := ExtractAdoptionFields(desired)
320316
if err != nil {
321317
return desired, ackerr.NewTerminalError(err)
322318
}
323319
if len(extractedFields) == 0 {
324-
// TODO(michaelhtm) figure out error here
320+
// TODO(michaelhtm) Here we need to figure out if we want to have an
321+
// error or not. should we consider accepting values from Spec?
322+
// And then we can let the ReadOne figure out if we have missing
323+
// required fields for a Read
325324
return nil, fmt.Errorf("failed extracting fields from annotation")
326325
}
327-
res := desired.DeepCopy()
328-
err = res.PopulateResourceFromAnnotation(extractedFields)
326+
resolved := desired.DeepCopy()
327+
err = resolved.PopulateResourceFromAnnotation(extractedFields)
329328
if err != nil {
330329
return nil, err
331330
}
332-
resolved := res
333331

334332
rlog.Enter("rm.EnsureTags")
335333
err = rm.EnsureTags(ctx, resolved, r.sc.GetMetadata())
@@ -340,37 +338,32 @@ func (r *resourceReconciler) handleAdoption(
340338
rlog.Enter("rm.ReadOne")
341339
latest, err := rm.ReadOne(ctx, resolved)
342340
if err != nil {
343-
return nil, err
341+
return latest, err
344342
}
345343

346-
if !r.rd.IsManaged(latest) {
347-
if err = r.setResourceManaged(ctx, rm, latest); err != nil {
348-
return nil, err
349-
}
344+
if err = r.setResourceManaged(ctx, rm, latest); err != nil {
345+
return latest, err
346+
}
350347

351-
// Ensure tags again after adding the finalizer and patching the
352-
// resource. Patching desired resource omits the controller tags
353-
// because they are not persisted in etcd. So we again ensure
354-
// that tags are present before performing the create operation.
355-
rlog.Enter("rm.EnsureTags")
356-
err = rm.EnsureTags(ctx, latest, r.sc.GetMetadata())
357-
rlog.Exit("rm.EnsureTags", err)
358-
if err != nil {
359-
return latest, err
360-
}
348+
// Ensure tags again after adding the finalizer and patching the
349+
// resource. Patching desired resource omits the controller tags
350+
// because they are not persisted in etcd. So we again ensure
351+
// that tags are present before performing the create operation.
352+
rlog.Enter("rm.EnsureTags")
353+
err = rm.EnsureTags(ctx, latest, r.sc.GetMetadata())
354+
rlog.Exit("rm.EnsureTags", err)
355+
if err != nil {
356+
return latest, err
361357
}
362358
r.rd.MarkAdopted(latest)
363359
rlog.WithValues("is_adopted", "true")
364360
latest, err = r.patchResourceMetadataAndSpec(ctx, rm, desired, latest)
365361
if err != nil {
366362
return latest, err
367363
}
368-
err = r.patchResourceStatus(ctx, desired, latest)
369-
if err != nil {
370-
return latest, err
371-
}
364+
372365
rlog.Info("Resource Adopted")
373-
return latest, requeue.NeededAfter(nil, 5)
366+
return latest, nil
374367
}
375368

376369
// reconcile either cleans up a deleted resource or ensures that the supplied
@@ -435,10 +428,18 @@ func (r *resourceReconciler) Sync(
435428
isAdopted := IsAdopted(desired)
436429
rlog.WithValues("is_adopted", isAdopted)
437430

438-
if r.cfg.FeatureGates.IsEnabled(featuregate.AdoptResources) {
439-
latest, err := r.handleAdoption(ctx, rm, desired, rlog)
440-
if err != nil {
441-
return latest, err
431+
if r.cfg.FeatureGates.IsEnabled(featuregate.ResourceAdoption) {
432+
if NeedAdoption(desired) && !r.rd.IsManaged(desired) {
433+
latest, err := r.handleAdoption(ctx, rm, desired, rlog)
434+
435+
if err != nil {
436+
// If we get an error, we want to return here
437+
// TODO(michaelhtm): Change the handling of
438+
// the error to allow Adopt or Create here
439+
// when supported
440+
return latest, err
441+
}
442+
return latest, nil
442443
}
443444
}
444445

pkg/runtime/util.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,30 +70,29 @@ func IsReadOnly(res acktypes.AWSResource) bool {
7070
return false
7171
}
7272

73-
// hasAdoptAnnotation returns true if the supplied AWSResource has an annotation
74-
// indicating that it should be adopted
75-
func hasAdoptAnnotation(res acktypes.AWSResource) bool {
73+
// GetAdoptionPolicy returns the Adoption Policy of the resource
74+
// defined by the user in annotation. Possible values are:
75+
// adopt-only | adopt-or-create
76+
// adopt-only keeps requing until the resource is found
77+
// adopt-or-create creates the resource if does not exist
78+
func GetAdoptionPolicy(res acktypes.AWSResource) string {
7679
mo := res.MetaObject()
7780
if mo == nil {
78-
// Should never happen... if it does, it's buggy code.
79-
panic("hasAdoptAnnotation received resource with nil RuntimeObject")
81+
panic("getAdoptionPolicy received resource with nil RuntimeObject")
8082
}
8183
for k, v := range mo.GetAnnotations() {
82-
if k == ackv1alpha1.AnnotationForceAdoption {
83-
return strings.ToLower(v) == "true"
84+
if k == ackv1alpha1.AnnotationAdoptionPolicy {
85+
return v
8486
}
8587
}
86-
return false
87-
}
8888

89-
func createOrAdopt(res acktypes.AWSResource) bool {
90-
return hasAdoptAnnotation(res) || IsAdopted(res)
89+
return ""
9190
}
9291

93-
// NeedAdoption returns true when the resource has
92+
// NeedAdoption returns true when the resource has
9493
// adopt annotation but is not yet adopted
9594
func NeedAdoption(res acktypes.AWSResource) bool {
96-
return hasAdoptAnnotation(res) && !IsAdopted(res)
95+
return GetAdoptionPolicy(res) != "" && !IsAdopted(res)
9796
}
9897

9998
func ExtractAdoptionFields(res acktypes.AWSResource) (map[string]string, error) {

pkg/runtime/util_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,26 +74,26 @@ func TestIsForcedAdoption(t *testing.T) {
7474
res := &mocks.AWSResource{}
7575
res.On("MetaObject").Return(&metav1.ObjectMeta{
7676
Annotations: map[string]string{
77-
ackv1alpha1.AnnotationForceAdoption: "true",
78-
ackv1alpha1.AnnotationAdopted: "false",
77+
ackv1alpha1.AnnotationAdoptionPolicy: "true",
78+
ackv1alpha1.AnnotationAdopted: "false",
7979
},
8080
})
8181
require.True(ackrt.NeedAdoption(res))
8282

8383
res = &mocks.AWSResource{}
8484
res.On("MetaObject").Return(&metav1.ObjectMeta{
8585
Annotations: map[string]string{
86-
ackv1alpha1.AnnotationForceAdoption: "true",
87-
ackv1alpha1.AnnotationAdopted: "true",
86+
ackv1alpha1.AnnotationAdoptionPolicy: "true",
87+
ackv1alpha1.AnnotationAdopted: "true",
8888
},
8989
})
9090
require.False(ackrt.NeedAdoption(res))
9191

9292
res = &mocks.AWSResource{}
9393
res.On("MetaObject").Return(&metav1.ObjectMeta{
9494
Annotations: map[string]string{
95-
ackv1alpha1.AnnotationForceAdoption: "false",
96-
ackv1alpha1.AnnotationAdopted: "true",
95+
ackv1alpha1.AnnotationAdoptionPolicy: "false",
96+
ackv1alpha1.AnnotationAdopted: "true",
9797
},
9898
})
9999
require.False(ackrt.NeedAdoption(res))
@@ -111,10 +111,10 @@ func TestExtractAdoptionFields(t *testing.T) {
111111
}`,
112112
},
113113
})
114-
114+
115115
expected := map[string]string{
116116
"clusterName": "my-cluster",
117-
"name": "ng-1234",
117+
"name": "ng-1234",
118118
}
119119
actual, err := ackrt.ExtractAdoptionFields(res)
120120
require.NoError(err)

0 commit comments

Comments
 (0)