kapp icon indicating copy to clipboard operation
kapp copied to clipboard

Strip kustomize-style name hash suffix

Open criztovyl opened this issue 2 years ago • 43 comments

What this PR does / why we need it:

This PR tries to demonstrate a way to implement the feature request from #81.

Which issue(s) this PR fixes:

Fixes #81

Does this PR introduce a user-facing change?

Adds an `StripNameHashSuffix` option to config, allowing to strip kustomize-style ConfigMap/Secret hash suffixes.

Additional Notes for your reviewer:

Review Checklist:
  • [x] Follows the developer guidelines
  • [x] Relevant tests are added or updated
  • [x] Relevant docs in this repo added or updated (none found)
  • [ ] Relevant carvel.dev docs added or updated in a separate PR and there's a link to that PR (will do that after first feedback here)
  • [x] Code is at least as readable and maintainable as it was before this change

Additional documentation e.g., Proposal, usage docs, etc.:


criztovyl avatar May 29 '22 09:05 criztovyl

@criztovyl, you must sign our contributor license agreement before your changes are merged. Click here to sign the agreement. If you are a VMware employee, read this for further instruction.

vmwclabot avatar May 29 '22 09:05 vmwclabot

Some thoughts on where to put the activation flag:

  • the suffix-strip activation should got to config, like templateRules
  • these are passed to VersionedResource (versioned resource type) from ChangeSetWithVersionedRes (change-set type)
  • the change-set type needs to be adjusted to pass the activation from config to the versioned resource
  • the change-set type does not know about config, it only gets the templateRules directly. thus the places using the change-set will need be adjusted to pass the activation. as the demonstration tests is on change-set level I'll not care about this yet.

While looking how to change the change-set type, it came to my understanding that the versioned resource type seems to be used only in change-set type, for the following two things:

  1. working with the name / resource itself (BaseNameAndVersion() etc.)
  2. updating the name on affected resources (UpdateAffected())

First, I'll need to adjust the places where it's used for 1. There it seems the change-set type instantiates the versioned-resource type ad-hoc/in-line, with the templateRules niled-out. It only instantiates with rules when actually calling the update for affected resources. This suggests the templateRules are only required when updating affected resources (i.e. 2.).

On the other hand the suffix-stripping flag is required in all the places the versioned-resource is currently instantiated ad-hoc. This means I need to change all those places. I'll move forward with that for the moment.

I am considering to extract the ad-hoc instantiation into a method in the change-set type. But generally I think it might be better to separate 1. from the above list into an own type, e.g. VersionedResourceName. That being a more invasive change, is another reason to, for now, simply changing the ad-hoc instantiations.

criztovyl avatar May 29 '22 11:05 criztovyl

I suspect a simple flag will not be enough, might need to be a more complex type, as kustomize allows you to disable the name-suffix-hash, so you might need to exclude certain CMs/Secrets.

It also affects only CMs/Secrets, I think my current implementation will also strip non-suffixes from e.g. deployments like name: my-deployment (i.e. stripping -deployment).

criztovyl avatar May 29 '22 11:05 criztovyl

Will look into some e2e tests now and await initial feedback. :)

criztovyl avatar May 29 '22 12:05 criztovyl

Hey! Thanks for the PR <3 Going over this and taking a closer look 🚀

100mik avatar May 31 '22 06:05 100mik

It also affects only CMs/Secrets, I think my current implementation will also strip non-suffixes from e.g. deployments like name: my-deployment (i.e. stripping -deployment).

this is my biggest concern as well. can we have some stricter way of determining what is "suffixed by kustomize" and what is not?

cppforlife avatar Jun 01 '22 18:06 cppforlife

@criztovyl Thank you so much for the PR. I am pretty new to kustomize space, so just trying to understand a bit more about the suffixHash...

Does the suffix get added to only the ConfigMap and Secrets created using the kustomize generators? Is there any way to determine if a resource has a suffix added by kustomize (using some annotation, etc,.) which we can use to remove the suffix. Or is there a way to disable the suffix (I noticed this option provided here, but not sure if this will work for all the resources)

praveenrewar avatar Jun 02 '22 13:06 praveenrewar

Thanks for the feedback :) I am working on this over the weekends, I'll answer your questions then.

criztovyl avatar Jun 02 '22 14:06 criztovyl

I was one of the first persons to upvote the issue https://github.com/vmware-tanzu/carvel-kapp/issues/81 many months ago and I have been thinking about this issue a lot since then.

I am sorry for being so negative, but I think this issue is close to impossible to solve.

Or is there a way to disable the suffix

Yes. You've already found disableNameSuffixHash: true. This is what we are using right now, but this has to be configured for each and every configMap/secret generator separately and is easily forgotten and also adds a lot of "noise" to the kustomization.yaml files.

Is there any way to determine if a resource has a suffix added by kustomize (using some annotation, etc,.) which we can use to remove the suffix.

No, there are no annotations or anything else to identify names of configMaps or secrets that have been rewritten by kustomize.

This is one of the main reason why I think this issue is next to impossible to solve with perfect accuracy. Kapp would not only have to remove the hash-suffixes from the configMaps and secrets, but also from all resources that are referencing these configMaps and secrets. Again, there a no annotations or anything to identify these references.

To make matters worse and which is probably the final nail in the coffin here: Similar to kapp, kustomize has a set of hardcoded template rules which are called name reference transformers in kustomize. Similar to kapp, users can provide additional transformers via kustomizeconfig.yaml files to enable kustomize to also rewrite configMap/secret references inside Custom Resources (CRs) that kustomize does not support out of the box. For example, we use this kustomizeconfig.yaml to let kustomize transform the name of a Secret reference inside a CR called ServersTransport:

nameReference:
  - kind: 'Secret'
    fieldSpecs:
      - kind: 'ServersTransport'
        path: 'spec/rootCAsSecrets'

If we want kapp to strip this hash-suffix from this CR, kapp needs to take this config into account to know about this reference:

apiVersion: kapp.k14s.io/v1alpha1
kind: Config
templateRules:
  - resourceMatchers:
      - apiVersionKindMatcher: {apiVersion: v1, kind: Secret}
    affectedResources:
      objectReferences:
        - path: [spec, rootCAsSecrets, {allIndexes: true}]
      resourceMatchers:
  - apiVersionKindMatcher: {apiVersion: traefik.containo.us/v1alpha1, kind: ServersTransport}

I don't know how I feel about that. These templateRules primarily exist so that kapp can add its own "version"-suffix to all resources that reference the given resource (a "Secret" in this example). But now we need these templateRules so that kapp can remove these suffixes that have been added by another tool. This feels... icky.

ChristianCiach avatar Jun 02 '22 15:06 ChristianCiach

Oh, true, I did not think about that consequence yet, thank you!

Actually my concern is just cosmetic, I dont necessarily want true versioning. I am happy if the diff shows two hash-suffixed CMs/Secrets as an update, not as delete + add.

For that I can ignore the referneces, they will show as updated anyway. So the stripping of the suffix should not go into final cluster state (as it would do with my current code), it should only be used to identifying potentially related resources.

I'll look into that.

criztovyl avatar Jun 02 '22 16:06 criztovyl

From my understanding the resources are diffed by grouping the old and new one using their name and then diffing these groups-of-two. Thus the challenge should be to "just" get the old and new hash-suffixed CMs/Secrets into the same group (i.e. update) instead of two separate one (delete+add).

The pieces are already there, used for actually versioned resources.

Spoken differently I want to build a VersionedResource that provides a UniqVersionedKey that strips the suffix, but where SetBaseName and UpdateAffected are no-op (return directly without updating resource name nor references).

I think that implementing this into the VersionedResource type is technically possible but will push it to it's edge, I intend to refactor that properly.

criztovyl avatar Jun 02 '22 17:06 criztovyl

Thank you so much for sharing all the insights @ChristianCiach.

Kapp would not only have to remove the hash-suffixes from the configMaps and secrets, but also from all resources that are referencing these configMaps and secrets

This is definitely a valid concern and I can't think of an easy way around it (maybe ytt would be helpful here).

@criztovyl Thank you again for giving this a shot. I will try to take a closer look at all the details again in the morning.

praveenrewar avatar Jun 02 '22 17:06 praveenrewar

So the stripping of the suffix should not go into final cluster state (as it would do with my current code), it should only be used to identifying potentially related resources.

as an interested observer (of this PR), this comment clears up some of my misunderstanding.

cppforlife avatar Jun 02 '22 20:06 cppforlife

This is what we are using right now, but this has to be configured for each and every configMap/secret generator separately and is easily forgotten and also adds a lot of "noise" to the kustomization.yaml files.

I have been thinking about this and I tried to use ytt overlays to add the versioned annotation and the disableNameSuffixHash option to all the secret generators and configmap generators. But I noticed that kustomize build command only accepts paths to directories and hence it's not possible to pipe the ytt output to kustomize. We would have to update the kustomization.yaml first and then use kustomize, so I guess it's not a viable option either.

praveenrewar avatar Jun 03 '22 13:06 praveenrewar

So the stripping of the suffix should not go into final cluster state (as it would do with my current code), it should only be used to identifying potentially related resources.

I see. So you want this change to be purely cosmetic, just to show an update operation on deploy instead of a delete and create.

Yes, this sounds a lot less invasive, since there is no need to rename any references in this case.

Just one more thing to keep in mind: It is unlikely, but when stripping (for example) dashboard-config-7bmh4kf599 to dashboard-config, it could conflict with a configMap that is actually named dashboard-config.

I still think this is nice to have, but keeping in mind that this is a purely cosmetic change, I still think it may not be worth it, depending on code complexity and future maintenance overhead.

ChristianCiach avatar Jun 08 '22 12:06 ChristianCiach

I still think it may not be worth it, depending on code complexity and future maintenance overhead.

Yes. I am doing this also for the fun of it -- should it get to complex I have no problem to accept the implementation does not fit :)

criztovyl avatar Jun 08 '22 13:06 criztovyl

Hi o/

I have implemented that the diff ignores the nameHashSuffix. The new TestChangeSet_StripKustomizeSuffix unit test should demonstrate what I want(ed): https://github.com/vmware-tanzu/carvel-kapp/blob/7973475f576cf399e7f7f4b67398360767b130b7/pkg/kapp/diff/change_set_with_versioned_rs_test.go#L187-L196

for me the following points are still open:

  • [x] refactor versioned resource type I would like to split it into two types, one for name-matching related stuff and one for name-and-reference-updating stuff. cf https://github.com/vmware-tanzu/carvel-kapp/pull/517#issuecomment-1140425656)
  • [x] customizable filter for which resources the suffix should not be trimmed or should be trimmed additonally I would like to implement this using resourceMatchers in (default) config.
  • [x] add e2e test for my use-case

Locally all unit tests succeed, e2e also look good.

GitHub tells me the Action workflows need approval from a maintainer, maybe those could also be activated? Then I don't run into funny works-on-my-machine stuff :)

For the VersioneResource refactoring I am considering extracting that into a dedicated PR and (re)basing the diff changes on that later. What you you think?

criztovyl avatar Jun 11 '22 19:06 criztovyl

customizable filter for which resources the suffix should not be trimmed or should be trimmed additonally I would like to implement this using resourceMatchers in (default) config.

Looking forward to seeing this :)

For the VersioneResource refactoring I am considering extracting that into a dedicated PR and (re)basing the diff changes on that later

Sure, we can have a separate PR. (The changes are still reviewable in the same PR so I am actually comfortable with both the options)

praveenrewar avatar Jun 13 '22 17:06 praveenrewar

@criztovyl, VMware has approved your signed contributor license agreement.

vmwclabot avatar Jun 22 '22 18:06 vmwclabot

finally took the time to start thinking about e2e test and directly uncovered a bug in the new code.

while the diff is correctly shown, the old CM is not deleted.

christoph@chschulz09:~/Documents/dev/carvel-kapp/test/e2e/res/kustomize/overlays/versioned2                                                                   
[criztovyl/strip-name-hash-suffix %= 173c57b]$ ../../../../../../kapp deploy -a versioned -f kapp.yml -c                                                                                                                                                                                                                     
Target cluster 'https://127.0.0.1:37067' (nodes: minikube)                                                                                                    
                                                                                                                                                              
@@ create configmap/config-map-798k5k7g9f (v1) namespace: default @@           
  ...                                                                                                                                                         
  1,  1   data:                                                                                                                                               
  2     -   foo: foo                                                                                                                                                                                                                                                                                                         
      2 +   foo: bar                                                                                                                                                                                                                                                                                                         
  3,  3   kind: ConfigMap                                                                                                                                     
  4,  4   metadata:                                                                                                                                           
  ...                                                                                                                                                         
  7,  7       kapp.k14s.io/app: "1656313846367027698"                                                                                                         
  8     -     kapp.k14s.io/association: v1.dacacaa0f2d3ae85e5dc5c636cc2c35e                                                                                                                                                                                                                                                  
      8 +     kapp.k14s.io/association: v1.ba177a572163dd4bcbee99aee2c7a5a6                                                                                                                                                                                                                                                  
  9,  9     managedFields:                                                                                                                                    
 10, 10     - apiVersion: v1                                                                                                                                  
                                                                                                                                                              
Changes                                                                                                                                                       
                                                                                                                                                              
Namespace  Name                   Kind       Age  Op      Op st.  Wait to    Rs  Ri                                                                           
default    config-map-798k5k7g9f  ConfigMap  -    create  -       reconcile  -   -                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                                                                             
Op:      1 create, 0 delete, 0 update, 0 noop, 0 exists                                                                                                                                                                                                                                                                      
Wait to: 1 reconcile, 0 delete, 0 noop                                                                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                                             
Continue? [yN]: y                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                             
9:12:19AM: ---- applying 1 changes [0/1 done] ----                                                                                                            
9:12:20AM: create configmap/config-map-798k5k7g9f (v1) namespace: default                                                                                     
9:12:20AM: ---- waiting on 1 changes [0/1 done] ----                                                                                                          
9:12:20AM: ok: reconcile configmap/config-map-798k5k7g9f (v1) namespace: default                                                                              
9:12:20AM: ---- applying complete [1/1 done] ----                                                                                                             
9:12:20AM: ---- waiting complete [1/1 done] ----                                                                                                              
                                                                               
Succeeded

christoph@chschulz09:~/Documents/dev/carvel-kapp/test/e2e/res/kustomize
[criztovyl/strip-name-hash-suffix= c0fd89c]$ kubectl get cm
NAME                     DATA   AGE
config-map-798k5k7g9f    1      7m39s
config-map-7tgk8db28b    1      9m3s
[...]

I'll have a look at that. In the output above kapp tells 1 create, 0 delete [...] I'll start with checking how kapp marks objects as deleted. :)

criztovyl avatar Jun 27 '22 07:06 criztovyl

while the diff is correctly shown, the old CM is not deleted.

Generally, for versioned resources, kapp keeps upto 5 versions (customisable using kapp.k14s.io/num-versions=int) and noop operation is used for the previous versions. I will have to take a look at the changes again to be sure, but I am guessing that something similar is happening here.

Also, I am wondering why the association label is getting changed.

P.S. There are some tests that are failing on the latest version of k8s (test-gh), so you might wanna rebase with develop once to get the latest changes which have a fix for them.

praveenrewar avatar Jun 28 '22 10:06 praveenrewar

I am wondering why the association label is getting changed.

I checked this and found that the association label is created on the input name of the CM. This means if you have an actual versioned resource, the name in input always is the same, the -ver-{n} is added later, with the association label already added. But with the name-suffix-hash the name in input changes, thus also the association label does.

Generally, for versioned resources, kapp keeps upto 5 versions (customisable using kapp.k14s.io/num-versions=int) and noop operation is used for the previous versions. I will have to take a look at the changes again to be sure, but I am guessing that something similar is happening here.

I have checked this with num-versions=1 and a third change -- then the first CM is deleted. But I suspect this only works with 1 version to keep; with more the only information kapp has for determining the order of name-suffix-strip CMs is the age.

criztovyl avatar Jul 10 '22 11:07 criztovyl

But with the name-suffix-hash the name in input changes, thus also the association label does.

I see. That makes sense.

But I suspect this only works with 1 version to keep; with more the only information kapp has for determining the order of name-suffix-strip CMs is the age.

That's true. With the current changes I think it would be randomly deleting any of the changes. But do you need to keep more than 1 versions for your use case?

praveenrewar avatar Jul 11 '22 17:07 praveenrewar

But do you need to keep more than 1 versions for your use case?

Actually, no, I thought about dropping any other version for suffix-stripped resources :)

criztovyl avatar Jul 11 '22 18:07 criztovyl

kapp now behaves like I want it; the diff is shown and the old CM is deleted. \o/

https://gist.github.com/criztovyl/85f594d455a2a413d6e20b0766303033

e2e seem to be running fine locally, there were some failures regarding wait rules, but recreating the kapp-test NS fixed those.

I have not addressed the changing association label yet, but I'll put that at the end of the backlog ^^

Next I'll check how to add some end-to-end tests, or maybe first some more unit tests. If I have that, I'll look into the VersionedResource refactoring.

criztovyl avatar Jul 17 '22 18:07 criztovyl

Finally took the time to write the e2e test, based on them I can now confidently make your suggested changes and/or do the refactorings. :)

criztovyl avatar Aug 16 '22 22:08 criztovyl

I have changed the e2e test as it failed on CI because the diff looks different. I dropped checking the diff now and only check there is one "update" diff and not two "delete and create" diffs.

I also made configurable which resources are covered by the name-hash-suffix-stripping, thus you can now exclude CMs or other resources which do not have a suffix (e.g. because it was disabled in kustomize or does not come from and configMapGenerator).

I plan next to restructure the commits so I have a base with failing tests on top of which I can maybe try different approaches to actually stripping the resources. I'll also look through the review comments, did not do that yet. :)

criztovyl avatar Aug 29 '22 20:08 criztovyl

I plan next to restructure the commits so I have a base with failing tests on top of which I can maybe try different approaches to actually stripping the resources. I'll also look through the review comments, did not do that yet. :)

Awesome, do let me know when you would want me to take another look at the PR :)

praveenrewar avatar Sep 01 '22 08:09 praveenrewar

I hope the tests are fixed now, I will focus on properly refactoring my changes now.

criztovyl avatar Sep 04 '22 14:09 criztovyl

@praveenrewar Could you review how I have implemented the configuration for my feature?

The config is used in the following place, added with commit https://github.com/vmware-tanzu/carvel-kapp/pull/517/commits/9807418550ed0827f69118fb131da5d7fe8973cb:

https://github.com/vmware-tanzu/carvel-kapp/blob/9807418550ed0827f69118fb131da5d7fe8973cb/pkg/kapp/diff/versioned_resource.go#L27-L29

criztovyl avatar Sep 04 '22 17:09 criztovyl