Skip to content

Commit

Permalink
Add read-only support for ACK resources via annotation
Browse files Browse the repository at this point in the history
This commit adds support for making ACK resources read-only via an
annotation. A new `services.k8s.aws/read-only` annotation can be
added to a resource to instruct the ACK service controller to not
create/patch/delete the underlying AWS resource.

If the annotation is set to "true", the reconciler will:
- return an error on create if the resource doesn't exist
- only patch metadata/spec for updates but not make cloud API calls
- skip deletion of the underlying resource when the Kubernetes resource
  is deleted

This allows adopting externally-managed resources into ACK for read-only
purposes like referencing values, without ACK attempting to manage the
resource lifecycle.

Signed-off-by: Amine Hilaly <[email protected]>
  • Loading branch information
a-hilaly committed Mar 26, 2024
1 parent f67ec48 commit c7d6e6f
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 2 deletions.
6 changes: 6 additions & 0 deletions apis/core/v1alpha1/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@ const (
// resource manager will leave the AWS resource intact when the K8s resource
// is deleted.
AnnotationDeletionPolicy = AnnotationPrefix + "deletion-policy"
// AnnotationReadOnly is an annotation whose value is a boolean indicating
// whether the resource is read-only. If this annotation is set to true on a
// CR, that means the user is indicating to the ACK service controller that
// the resource is read-only and should not be created/patched/deleted by the
// ACK service controller.
AnnotationReadOnly = AnnotationPrefix + "read-only"
)
3 changes: 3 additions & 0 deletions pkg/errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ var (
// information that the resource being checked for existence was
// previously-created out of band from ACK
AdoptedResourceNotFound = fmt.Errorf("adopted resource not found")
// ReadOnlyResourceNotFound is like NotFound but provides the caller with
// information that the resource is on read-only mode and was not found
ReadOnlyResourceNotFound = fmt.Errorf("read-only resource not found")
// MissingNameIdentifier indicates an unexpected nil name identifier pointer
MissingNameIdentifier = fmt.Errorf("expected name identifier, found nil")
// NotAdoptable is to indicate the current resource has been explicitly
Expand Down
17 changes: 15 additions & 2 deletions pkg/runtime/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ func (r *resourceReconciler) reconcile(
) (acktypes.AWSResource, error) {
if res.IsBeingDeleted() {
// Determine whether we should retain or delete the resource
if r.getDeletionPolicy(res) == ackv1alpha1.DeletionPolicyDelete {
if r.getDeletionPolicy(res) == ackv1alpha1.DeletionPolicyDelete && !IsReadOnly(res) {
// Resolve references before deleting the resource.
// Ignore any errors while resolving the references
resolved, _, _ := rm.ResolveReferences(ctx, r.apiReader, res)
Expand Down Expand Up @@ -284,6 +284,9 @@ func (r *resourceReconciler) Sync(
isAdopted := IsAdopted(desired)
rlog.WithValues("is_adopted", isAdopted)

isReadOnly := IsReadOnly(desired)
rlog.WithValues("is_read_only", isReadOnly)

rlog.Enter("rm.ResolveReferences")
resolved, hasReferences, err := rm.ResolveReferences(ctx, r.apiReader, desired)
rlog.Exit("rm.ResolveReferences", err)
Expand Down Expand Up @@ -311,13 +314,23 @@ func (r *resourceReconciler) Sync(
if isAdopted {
return nil, ackerr.AdoptedResourceNotFound
}
// NOTE(a-hilaly): When the time comes to support adopting resources
// using annotations, we will need to think a little bit more about
// the case where user, wants to create-or-adopt a resource, but the
// and also sets the resource to be read-only.
if isReadOnly {
return nil, ackerr.ReadOnlyResourceNotFound
}
if latest, err = r.createResource(ctx, rm, resolved); err != nil {
return latest, err
}
} else {
} else if !isReadOnly {
if latest, err = r.updateResource(ctx, rm, resolved, latest); err != nil {
return latest, err
}
} else if isReadOnly {
latest, err = r.patchResourceMetadataAndSpec(ctx, rm, desired, latest)
return latest, err
}
// Attempt to late initialize the resource. If there are no fields to
// late initialize, this operation will be a no-op.
Expand Down
16 changes: 16 additions & 0 deletions pkg/runtime/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,19 @@ func IsSynced(res acktypes.AWSResource) bool {
}
return false
}

// IsReadOnly returns true if the supplied AWSResource has an annotation
// indicating that it is in read-only mode.
func IsReadOnly(res acktypes.AWSResource) bool {
mo := res.MetaObject()
if mo == nil {
// Should never happen... if it does, it's buggy code.
panic("IsReadOnly received resource with nil RuntimeObject")
}
for k, v := range mo.GetAnnotations() {
if k == ackv1alpha1.AnnotationReadOnly {
return strings.ToLower(v) == "true"
}
}
return false
}

0 comments on commit c7d6e6f

Please sign in to comment.