-
Notifications
You must be signed in to change notification settings - Fork 180
Create adoption reconciler #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create adoption reconciler #2
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great start, @RedbackThomson! Thanks!
Some suggestions and comments inline for you to ponder.
| reconciler | ||
| rmf acktypes.AWSResourceManagerFactory | ||
| rd acktypes.AWSResourceDescriptor | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. This is exactly the kind of polymorphism I was thinking of when we chatted about the implementation. ++
| // ACKAdoptionReconciler | ||
| type ACKAdoptionReconciler interface { | ||
| ACKReconciler | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need for this interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just futureproofing. But not at this stage, no
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, let's leave it out for now, then.
| MarkUnmanaged(AWSResource) | ||
| // MarkAdopted places descriptors on the custom resource that indicate the | ||
| // resource was not created from within ACK. | ||
| MarkAdopted(AWSResource) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be fine in the resource descriptor interface. Not sure whether in the future the process of marking adoption will mean setting AWS tags on the resource. If it does, then this may need to move to the AWSResourceManager interface.... we'll just have to wait and see.
| # if controller_gen_version_equals "v0.4.0"; then | ||
| # echo "controller-gen is at version 0.4.0" | ||
| # fi | ||
| k8s_controller_gen_version_equals() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unused I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I was thinking of checking within the build-runtime.sh script, again. Since there is a chance someone is calling the script directly, rather than through any of the scripts in community. But I don't like the code duplication.
pkg/types/aws_resource.go
Outdated
| // SetObjectMeta sets the ObjectMeta field for the resource | ||
| SetObjectMeta(meta metav1.ObjectMeta) | ||
| // SetARN will set the ARN field in the resource's metadata status field | ||
| SetARN(*ackv1alpha1.AWSResourceName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What we need is an interface that will help the AWSResourceManager determine which field in the acktypes.AWSResource's underlyihng Spec or Status struct maps to each field in a ackcorev1alpha1.AWSIdentifiers struct. We already know that the ackcorev1alpha1.AWSIdentifiers.ARN field maps to the Status.ACKMetadata.ARN field because we standardize on that. What we don't really know is which field in the Spec corresponds to the ackcorev1alpha1.AWSIdentifiers.Name and ackcorev1alpha1.AWSIdentifiers.ID field.
Something like this might work?
// AWSIdentifierFieldMapper helps a resource manager determine which fields
// in a resource represent name or generated IDs.
type AWSIdentifierFieldMapper interface {
// NameField returns the path to the field in the underlying resource's `Spec`
// or `Status` struct that represents the resource's name. This will be empty
// if there is no name field for the resource.
NameField() string
// IDField returns the path to the field in the underlying resource's `Spec`
// or `Status` struct that represents the resource's AWS-generated ID field.
// This will be empty if there is no such field for the resource.
IDField() string
}We could then have the AWSResourceDescriptor interface have a method that returns a AWSIdentifierFieldMapper-implementing struct:
type AWSResourceDescriptor interface {
// ...
// IdentifierFieldMapper returns an `AWSIdentifierFieldMapper` that instructs callers
// which fields in the `AWSResource` represent name or generated IDs.
IdentifierFieldMapper() AWSIdentifierFieldMapper
}Furthemore, instead of a SetNameField and SetARN method (and I presume you would eventually want a SetID method?) on the AWSResource interface above, how about just adding a SetIdentifiers method that accepts a single parameter of type ackcorev1alpha1.AWSIdentifiers? That way, the AdoptedResourceReconciler can construct a new AWSResource and set its identifier fields using a single method.
So, instead of this code that you have above in the AdoptedResourceReconciler:
// Set spec fields prior to reading
if desired.Spec.AWS.Name != nil {
readableResource.SetNameField(*desired.Spec.AWS.Name)
} else if desired.Spec.AWS.ID != nil {
readableResource.SetNameField(*desired.Spec.AWS.ID)
} else if desired.Spec.AWS.ARN != nil {
readableResource.SetARN(desired.Spec.AWS.ARN)
} else {
return fmt.Errorf("must provide at least one value for identifier")
}you could just do this instead:
readableResource.SetIdentifiers(*desired.Spec.AWS)and let the plumbing inside the controller's pkg/resource/$RESOURCE/descriptor.go and pkg/resource/$RESOURCE/resource.go files use the AWSIdentiferFieldMapper to determine the right fields in the underlying resource to pull out.
| // the resource into its management). If this annotation is set to false on | ||
| // a CR, that means the user expects the ACK service controller to create | ||
| // the backend AWS service API resource. | ||
| AnnotationAdopted = AnnotationPrefix + "adopted" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Been thinking more about this annotation and whether we really need it. I'm not so sure we do, at least in a boolean form. A couple questions that highlight my concerns:
-
If this annotation is not present on a CR, what does that mean?
-
Above, it states "If this annotation is set to false on a CR, that means the user expects the ACK service controller to create the backend AWS service API resource." But this isn't really true, right? It's not the annotation's presence or lack of presence that dictates whether the service controller takes some action. Instead, it's the creation of an
AdoptedResourceCR explicitly by the Kubernetes user that is the signal for a service controller to grab the latest observed state of the referred-to resource instead of calling the API's Create operation. -
Given 2), would it make more sense to add an annotation called
services.k8s.aws/adopted-onthat is a timestamp of when theAdoptedResourceReconcilersuccessfully created the resource CR after pulling the latest observed state from the AWS API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I could see more value in this becoming a timestamp. It was initially a boolean so that you could easily run a filter of services.k8s.aws/adopted=='true' on all resource annotations, but I understand that timestamp probably provide that plus more.
This annotation isn't being used by any system, it really is just informational to the user.
| const ( | ||
| // ConditionTypeAdopted indicates that the adopted resource custom resource | ||
| // has been successfully reconciled and the target has been created | ||
| ConditionTypeAdopted ConditionType = "ACK.Adopted" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this condition to be applied to the AdoptedResource CR or to the target resource CR? If the latter, then perhaps we don't need a services.k8s.aws/adopted-on annotation at all, since the Condition has a timestamp assocated with its creation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition is currently being applied to the AdoptedResource CR to describe to the user that it has essentially reached the terminal condition.
88e6c2f to
ba8304d
Compare
Rename ACKReconciler to AWSResourceReconciler
ba8304d to
67f49bb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm eager to see e2e tests for this functionality. :)
| // GetResourceManagerFactories returns the map of resource manager | ||
| // factories, keyed by the GroupKind of the resource managed by the resource | ||
| // manager produced by that factory | ||
| func (c *serviceController) GetResourceManagerFactories() map[string]acktypes.AWSResourceManagerFactory { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat !! Very nice to see the adoption reconciler in action! 👍
I left few non-blocking comments/questions
| r.patchAdoptedCondition(ctx, res, err) | ||
| return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error return value is not check here, did you mean return r.patchAdoptedCondition(ctx, res, err) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct. This is meant to wrap an error, ensuring that it is implanted into the adopted resource before we raise it. It's a wrapper around return err in other methods.
| rec := NewAdoptionReconciler(c, c.log, cfg, c.metrics) | ||
| if err := rec.BindControllerManager(mgr); err != nil { | ||
| return err | ||
| } | ||
| c.adoptionReconciler = rec |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit/QQ: Would it be valuable to add a flag to enable/disable the adoption reconciler? i'm thinking about --enable-adoption-reconciler and default it to true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since resources can be opted out of adoption (via the generator.yaml), I'm OK with adding a CLI flag after this first patch is merged. We can even consider a CRD that allows the service controller to be configured without restarting the service controller -- and that CRD could allow configuring things like the resources that should be adoptable.
|
|
||
| // handleReconcileError will handle errors from reconcile handlers, which | ||
| // respects runtime errors. | ||
| func (r *adoptionReconciler) handleReconcileError(err error) (ctrlrt.Result, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: similar to resourceReconciler.handleReconcileError - in the future we can merge them
apis/core/v1alpha1/types.go
Outdated
| type AdoptionStatus string | ||
|
|
||
| const ( | ||
| AdoptionStatus_Adopted AdoptionStatus = "Adopted" | ||
| AdoptionStatus_Error AdoptionStatus = "Error" | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QQ: i can't find where these constants are used, are they?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I don't believe this is used anywhere and probably should be removed.
| ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" | ||
| ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" | ||
| ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" | ||
| ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" | ||
| "github.com/aws-controllers-k8s/runtime/pkg/requeue" | ||
| ackrtcache "github.com/aws-controllers-k8s/runtime/pkg/runtime/cache" | ||
| acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" | ||
| "k8s.io/apimachinery/pkg/runtime/schema" | ||
| "sigs.k8s.io/controller-runtime/pkg/client" | ||
| k8sctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tiny nit: running goimports can help organizing imports
| // we look for the namespace associated region, if that is set we use it. Finally | ||
| // if none of these annotations are set we use the use the region specified in the | ||
| // configuration is used | ||
| func (r *adoptionReconciler) getRegion( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same thing as for handleReconcileError
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RedbackThomson just remove the unused string constants for adopted status and I'll merge this PR.
apis/core/v1alpha1/types.go
Outdated
| type AdoptionStatus string | ||
|
|
||
| const ( | ||
| AdoptionStatus_Adopted AdoptionStatus = "Adopted" | ||
| AdoptionStatus_Error AdoptionStatus = "Error" | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I don't believe this is used anywhere and probably should be removed.
| rec := NewAdoptionReconciler(c, c.log, cfg, c.metrics) | ||
| if err := rec.BindControllerManager(mgr); err != nil { | ||
| return err | ||
| } | ||
| c.adoptionReconciler = rec |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since resources can be opted out of adoption (via the generator.yaml), I'm OK with adding a CLI flag after this first patch is merged. We can even consider a CRD that allows the service controller to be configured without restarting the service controller -- and that CRD could allow configuring things like the resources that should be adoptable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
muy bueno :)
Issue #, if available:
Description of changes:
ACKReconcilerto allow for new reconciler types other thanAWSResourceReconcilerBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.