Skip to content

Conversation

@RedbackThomson
Copy link
Contributor

@RedbackThomson RedbackThomson commented Feb 25, 2022

Issue #, if available: aws-controllers-k8s/community#740

Description of changes:
Creates a new FieldExport CRD as outlined this proposal. Also creates a new reconciler that reconciles changes to FieldExport and changes to any ACK resource recognised by the current controller.

The path for reconciling a FieldExport:

  1. Ensure the FieldExport Source resource group matches the current controller's group
  2. Mark as managed (add finalizer)
  3. Describe the source object
    3a. If no source object is found, stop reconciling
  4. Use the gojq library to parse the unstructured source object
  5. Write to ConfigMap/Secret

The path when any ACK resource is updated:

  1. Ensure the resource has the synced condition set to true
  2. List and filter all FieldExport types in the namespace that reference the current resource as its Source
  3. For each matching FieldExport do steps 3 and 4 from the previous path

The reconciler converts all primitive types into strings (ConfigMap is a string-string map, Secret is a string-byte[] map). If the jq query returns a list of results, we will only take the first result (this PR does not support exporting slices/maps). If the jq query returns a struct, we will not take any action, treating the query as a failure.

The reconciler will not delete any values from the ConfigMap or Secret. If the referenced field is removed from the object, the reconciler will no-op, rather than overwriting the data with an empty string. When the FieldExport is deleted, the ConfigMap or Secret will also be left untouched - meaning it contains whatever was last written to it.

Upon creating a FieldExport CR, the reconciler will initially attempt to export the field. If it fails, it will not attempt to retry. After the first sync, the reconciler will only wait until the ResourceVersion of the source object changes for it to trigger a new update.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Copy link
Collaborator

@jaypipes jaypipes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good, @RedbackThomson! A few minor suggestions inline along with one major comment about the handling of Recoverable conditions...

// NamespacedTargetKubernetesResource provides all the values necessary to identify an ACK
// resource of a given type (within the same namespace).
type NamespacedTargetKubernetesResource struct {
metav1.GroupKind `json:""`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should leave off the json:"" if you want to "inline" the GroupKind's fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other way around. Including the json tag inlines it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what to say haha. The AdoptedResource CRD remains the same if I do it this way - you can check the diff to verify.

}

// FieldExportOutputSelector provides the values necessary to identify the
// output path for a field export (within the same namespace).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is "(within the same namespace)", then a) the name of the struct should begin with Namespaced to align with the other namespace-scoped structs above like NamespacedTargetKubernetesResource and b) why does this struct have an optional Namespace field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this comment is incorrect. The output can be into a separate namespace. The default is the same namespace (which is why namespace is optional).

type AdoptedResourceSpec struct {
// +kubebuilder:validation:Required
Kubernetes *TargetKubernetesResource `json:"kubernetes"`
Kubernetes *AdoptedResourceTarget `json:"kubernetes"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is going to break any existing ACK controller, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just been renamed. The only thing that changes are the docstrings

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing AdoptedResource CRs in an existing installation of a controller would fail to be read into the controller, no? Since the over-wire format of the schema would be different...

Copy link
Contributor Author

@RedbackThomson RedbackThomson Mar 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Protobuf does not care about the field names. As long as I don't mess with the tag numbers, which I believe are auto-generated based on the order of the field, then I should be okay. Citation: https://stackoverflow.com/questions/45431685/protocol-buffer-does-changing-field-name-break-the-message

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Protobuf does not care about the field names. As long as I don't mess with the tag numbers, which I believe are auto-generated based on the order of the field, then I should be okay. Citation: https://stackoverflow.com/questions/45431685/protocol-buffer-does-changing-field-name-break-the-message

You aren't changing the name of the field above, though... You're changing the data type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reached out to Ellis about this:

[Nick] I feel that if the CRD is identical, there shouldn't be a difference?
[Ellis] The API Server serializes it using the CRD
[Ellis] Exactly
[Ellis] The APIServer isn't aware of your go struct, just the CRD
[Ellis] We do this regularly in karpenter w/ our go types

So we don't need to worry about etcd serialization. Just that we are maintaining the same contract with the API server, which is the CRD. I have confirmed that the CRD remains untouched.

Copy link
Contributor

@vijtrip2 vijtrip2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work! Left some minor comments.

from acktypes.AWSResource,
desired ackv1alpha1.FieldExport,
) error {
r.clearConditions(ctx, &desired)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor Suggestion: This will add extra patchStatus call to API server. I think conditions can be handled without this extra call by keeping a copy of desired in memory, updating that copy and then patch once at the end using desired as base.

The approach may look like:
a) Create latest by deepCopying desired.
b) clearConditions on latest in memory (do not patch)
c) Use latest inside the Sync function and set Apt conditions (Recoverable, Terminal, etc) in memory
d) PatchStatus at the very end, using desired as "base" .

The above approach will only make single call to APIServer for status patching. Currently there is one call for clearing condition and another per condition.

Copy link
Collaborator

@jaypipes jaypipes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting there, @RedbackThomson thank you! Left a number of suggestions inline for ya.

type AdoptedResourceSpec struct {
// +kubebuilder:validation:Required
Kubernetes *TargetKubernetesResource `json:"kubernetes"`
Kubernetes *AdoptedResourceTarget `json:"kubernetes"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Protobuf does not care about the field names. As long as I don't mess with the tag numbers, which I believe are auto-generated based on the order of the field, then I should be okay. Citation: https://stackoverflow.com/questions/45431685/protocol-buffer-does-changing-field-name-break-the-message

You aren't changing the name of the field above, though... You're changing the data type.

Copy link
Collaborator

@jaypipes jaypipes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RedbackThomson the whole fieldExportReconciler single reconciler struct for both source resource and FieldExport resources is still rubbing me the wrong way. Please see my comment inline about having two reconciler structs, one for FieldExports and another for source resources.

// Handle query errors
if err, ok := result.(error); ok {
if err != nil {
return nil, &terminalError{err: errors.Wrap(err, ackerr.FieldExportQueryFailed.Error())}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not put terminalError into pkg/errors and actually define pkg/errors.FieldExportQueryFailed as wrapping terminalError?

Copy link
Collaborator

@jaypipes jaypipes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's enough good here that I'm willing to address a few things about the fieldExportReconciler struct in followup PRs. 👍

@jaypipes
Copy link
Collaborator

/lgtm

@ack-bot ack-bot added the lgtm Indicates that a PR is ready to be merged. label Mar 18, 2022
@ack-bot
Copy link
Collaborator

ack-bot commented Mar 18, 2022

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jaypipes, RedbackThomson, vijtrip2

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:
  • OWNERS [RedbackThomson,jaypipes,vijtrip2]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ack-bot ack-bot merged commit 4c4d5cb into aws-controllers-k8s:main Mar 18, 2022
@RedbackThomson RedbackThomson deleted the field-exporter branch March 18, 2022 21:03
@acornett21
Copy link
Contributor

@RedbackThomson @jaypipes Sorry I'm late on this one, but is this a CRD that would be packaged with every controller just like the AdoptedResource correct? If so then mitigation work in the runtime and in the code-generator should be accounted for to not package this CRD. If I'm miss understanding this apologies.

@RedbackThomson
Copy link
Contributor Author

@RedbackThomson @jaypipes Sorry I'm late on this one, but is this a CRD that would be packaged with every controller just like the AdoptedResource correct? If so then mitigation work in the runtime and in the code-generator should be accounted for to not package this CRD. If I'm miss understanding this apologies.

Correct. This CRD is handled exactly in the same way as AdoptedResource. The mitigation work we put in for OLM to not package it, I think we did that for the common CRD directory? Hopefully that code already removes this new CRD as well.

@acornett21
Copy link
Contributor

@RedbackThomson Looking again at this PR and the code we have here I think adding this would be covered by whats in place. Can you please take a look and confirm? Thanks!

@RedbackThomson
Copy link
Contributor Author

@RedbackThomson Looking again at this PR and the code we have here I think adding this would be covered by whats in place. Can you please take a look and confirm? Thanks!

Exactly! I tried to future proof it, and I think this still holds.

@acornett21
Copy link
Contributor

Yeah, It looks that way, missed it on the first pass of looking at this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants