Skip to content
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

Strip kustomize-style name hash suffix #517

Open
wants to merge 53 commits into
base: develop
Choose a base branch
from

Conversation

criztovyl
Copy link

@criztovyl criztovyl commented May 29, 2022

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:
  • Follows the developer guidelines
  • Relevant tests are added or updated
  • 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)
  • Code is at least as readable and maintainable as it was before this
    change

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


@vmwclabot
Copy link

@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.

@criztovyl
Copy link
Author

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
Copy link
Author

criztovyl commented May 29, 2022

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
Copy link
Author

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

@100mik
Copy link
Contributor

100mik commented May 31, 2022

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

@renuy renuy removed their request for review May 31, 2022 06:14
@cppforlife
Copy link
Contributor

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?

@praveenrewar
Copy link
Member

@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)

@criztovyl
Copy link
Author

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

@ChristianCiach
Copy link

ChristianCiach commented Jun 2, 2022

I was one of the first persons to upvote the issue #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.

@criztovyl
Copy link
Author

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
Copy link
Author

criztovyl commented Jun 2, 2022

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.

@praveenrewar
Copy link
Member

praveenrewar commented Jun 2, 2022

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.

@cppforlife
Copy link
Contributor

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.

@praveenrewar
Copy link
Member

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.

@ChristianCiach
Copy link

ChristianCiach commented Jun 8, 2022

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.

@criztovyl
Copy link
Author

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
Copy link
Author

criztovyl commented Jun 11, 2022

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:

  • 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 Strip kustomize-style name hash suffix #517 (comment))
  • 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.
  • 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?

@praveenrewar
Copy link
Member

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)

@vmwclabot
Copy link

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

@criztovyl
Copy link
Author

criztovyl commented Jun 27, 2022

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. :)

@praveenrewar
Copy link
Member

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.

@criztovyl
Copy link
Author

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
Copy link
Author

Ah, forgot to look into the Enabled/EnableKustomizeResourceMatchers.

@criztovyl criztovyl requested review from praveenrewar and removed request for cppforlife and 100mik September 12, 2022 08:41
@criztovyl criztovyl marked this pull request as ready for review September 12, 2022 08:41
@praveenrewar
Copy link
Member

Hey @criztovyl ! Sorry I didn't get a chance to look at the changes properly, will get it to it soon :)

@criztovyl
Copy link
Author

ok, no worries :)

Copy link
Member

@praveenrewar praveenrewar left a comment

Choose a reason for hiding this comment

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

For most of the code that simply means changing the signatures and occasionally throwing in a .res or replacing ad-hoc VRes instantiations.

I am still thinking about this. (I do like the idea, but .res is something that is making me think more about it.)

pkg/kapp/config/conf.go Outdated Show resolved Hide resolved
pkg/kapp/diff/change_set_with_versioned_rs.go Outdated Show resolved Hide resolved
the operation is "add" because we're only manipulating the "add" diff to
show the difference to the previous version
preparation for dropping the enabled flag altogether; I have tried both at once
before and the matchers completely broke, so doing the complex part (no
matchers matching nothing) first with only *toggling* the default to
enabled instead of directly throwing it out.
it seems the field in config does not need to be an object anymore,
could as well be a (by default nil) list of matchers, but let's not
touch too much at once.
@criztovyl
Copy link
Author

the mere presence or absence of a resource matcher can be used to decide if the suffix needs to be trimmed for a resource or not

I have now implemented it that way.

Completely neglected docs until now, will look into that next. :)

@criztovyl
Copy link
Author

criztovyl commented Sep 29, 2022

While looking into the docs I realized the name of this feature, which was not very good in the beginning, after the iterations this PR went through, went bad...

The central part of this PR now is the alternative implementation of VersionResource (now an interface) for resources with hash suffixes, HashSuffixResource.

As such, what do you think about renaming everything to that? Maybe you have a better name in mind?

HashSuffixResource HashSuffixResource (same)
stripnamehashsuffix{,_test}.go hashsuffix_resource{,_test}.go
conf.StripHashNameSuffixConfig conf.HashSuffixDiffConfig (conf.HashSuffixDiffRules?)
ctldiff.stripNameHashSuffixConfig ctldiff.hashSuffixDiffConfig(ctldiff.hashSuffixDiffRules?)
ctlconf.StripHashNameSuffixConfig(s) ctlconf.HashSuffixDiffConfig(s) (ctlconf.HashSuffixDiffRules(List)?)

@praveenrewar
Copy link
Member

As such, what do you think about renaming everything to that?

Renaming definitely makes sense to me, but I will have to take a closer look at the changes (planning to do that tomorrow) to be able to comment on all the names.

Copy link
Member

@praveenrewar praveenrewar left a comment

Choose a reason for hiding this comment

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

Apologies for not being able to review this completely yet. I am still going through it, but thought of hitting the submit button once.

pkg/kapp/config/default.go Outdated Show resolved Hide resolved
pkg/kapp/config/config.go Outdated Show resolved Hide resolved
pkg/kapp/config/conf.go Outdated Show resolved Hide resolved
pkg/kapp/config/conf.go Outdated Show resolved Hide resolved
pkg/kapp/diff/stripnamehashsuffix.go Outdated Show resolved Hide resolved
pkg/kapp/diff/stripnamehashsuffix_test.go Outdated Show resolved Hide resolved
// N.B. if no config specifies any matchers (the default), then
// allMatchers will stay uninitialized/nil and the AndMatcher will never
// match, effectively disabling suffix strip.
result.ResourceMatcher = ctlres.AndMatcher{Matchers: allMatchers}
Copy link
Member

Choose a reason for hiding this comment

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

I am still trying to understand why this is required. Would you be able to share a couple of scenarios that you are trying to cover, maybe we can find an alternative to it.

Copy link
Author

Choose a reason for hiding this comment

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

I was trying to allow include / exclude semantics.

Rethinking that it probably is better to have explicit fields for include and exclude matchers:

stripNameHashSuffix: # hashSuffixDiffConfig
  include:
  - apiVersionKindMatcher: {apiVersion: v1, kind: ConfigMap}
  - apiVersionKindMatcher: {apiVersion: v1, kind: Secret}
  exclude:
  - kindNamespaceNameMatcher: {kind: ConfigMap, name: foo},

for the sake of discussion (RE) I intentionally chose to not make the config a slice here:
in the end the include/exclude lists will each be merged into a single include/exclude list; there I assume that a list of include+exclude objects does not really add anything besides a layer of merging.
It might make sense in context of consistency, though, as many of the other config fields (template rules, etc) are also lists.

Copy link
Member

Choose a reason for hiding this comment

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

We already have a notMatcher present. Do you think that will be useful instead of exclude?
So, this how the matchers currently work.
By default, any matcher is used. So you can just have something like this to match ConfigMaps and Secrets.

stripNameHashSuffix:
- resourceMatchers:
  - apiVersionKindMatcher: {apiVersion: v1, kind: ConfigMap}
  - apiVersionKindMatcher: {apiVersion: v1, kind: Secret}

If you want to exclude something, then you can use an andMatcher (with anyMatcher and notMatcher nested inside), something like this. So I think we can rely on the existing capabilities matchers.

Copy link
Author

Choose a reason for hiding this comment

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

The challenge then is aggregating such nested "and: (any , not)" matchers across configurations.

Copy link
Member

Choose a reason for hiding this comment

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

across configurations

Ideally we would want to have one Config only (although we could add as many as we want). Do you have any specific scenario where we would need multiple Configs?

Copy link
Author

Choose a reason for hiding this comment

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

I thought this over, no use-case springs to mind; Ii wanted to include it for completeness and the include/exclude was the latest pragamtic way I found to implement it.

I would then explicitly implement it so that only the suffix-config from last kapp-config is used :)

@criztovyl
Copy link
Author

#571 introduced a conflict on func (d ChangeSetWithVersionedRs) groupResources([]ctlres.Resource):
it moved it to func newGroupedVersionedResources(...) and uses it via diff.existingResourcesMap.
But I changed the method to use (and return) []VersionedResource, instead of instantiating VersionedResource ad-hoc.

To resolve that conflict I think I need to implement the suggestion from #571 (comment), extracting existingVersionedResources and newVersionedResources to a new struct.

I am thinking about something like below, what do you think?

// cmd/app/deploy.go
// func (o *DeployOptions) calculateAndPresentChanges(...)
//...
diffResources := ctldiff.NewDiffResourceFactory(existingResource, newResources,
    conf.TemplateRules(), conf.StripNameHashSuffixConfigs()).NewDiffResources()

err := ctldiff.NewRenewableResource(diffResources).Prepare()
// ...

changes, err := ctldiff.NewChangeSetWithVersionedRs(diffResources,
    opts ChangeSetOps, changeFactory ChangeFactory).Calculate()
// ...

with:

package diff

type DiffResources struct { // or any other name, only was the first one that came to my mind ^^
    ExisitingResources, NewResources versionedResources
    ExistingResourcesGrouped map[string][]VersionedResource
    // NB: While RenewableResources wants a map[string]ctlres.Resource
    // it should also be able to use [-1]VersionedResource.Res().
}

type DiffResourcesFactory struct { // or VersionedResourcesFactory?
    // attributes extracted from ChangeSetWithVersionedRs, only used by the extracted methods (only).
    existingRs, newRs []ctlres.Resource
    rules []ctlconf.TemplateRules
    stripNameHashSuffixConfig stripNameHashSuffixConfig
    // extracted attributes end
}

func NewDiffResourcesFactory(exisitingRs, newRs []ctlres.Resource,
    rules []ctlconf.TemplateRule,
    stripNameHashSuffixConfigs []ctlconf.StripNameHashSuffixConfig) DiffResourcesFactory {

    return DiffResourcesFactory{existingRs, newRs,
        rules, stripNameHashSuffixConfigFromConf(stripNameHashSuffixConfigs)}
}

func (d DiffResourcesFactory) NewDiffResources() DiffResources {
    result := DiffResources{}
    result.ExistingResources = d.existingVersionedRs()
    result.ExistingResourcesGrouped = d.existingVersionedRsGrouped()
    result.NewResources = d.newVersionedRs()
    return result
}

// methods extracted from ChangeSetWithVersionedRs

func (d DiffResourcesFactory) existingVersionedRs() versionedResources {
    // existingVersionedResources
}

func (d DiffResourcesFactory) existingVersionedRsGrouped() map[string][]VersionedResource {
    // groupResources / newGroupedVersionedResources
}

func (d DiffResourcesFactory) newVersionedRs() versionedResources {
    // newVersionedResources
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Detect ConfigMap change from kustomize smartly
6 participants