-
Notifications
You must be signed in to change notification settings - Fork 228
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
Special behavior for generators #2528
Comments
@mengqiy what is the easiest way to support this in function catalog.
Thinking of some possible options here:
3rd option is the least disruptive and easy to reason with.
some early thoughts:
|
I realized, our SDKs can also make writing |
This is the direction I think would make the most sense, and would be the simplest for other functions to reason about.
Yes, mainly I think we need an
|
Additional context #2435 |
kustomize has issues with the generator pattern also: |
Ok, so I am adding this caveat after writing everything below, which is just a sort of thought process thinking through some of these issues, and I am still not clear on how we achieve this and don't have time to continue thinking about it now. If the musings below are not useful, then feel free to ignore them. Perhaps you already have some more ideas on a better way to do this. I have a use case for a variant generator that would, for example, punch out a set of variants of a workload that are specialized to particular clusters. It works something like this: Variant Generator Function
Behavior
I'd like the user to be able to subsequently modify An to produce An'. When the function is re-run I do not want to overwrite those edits. This can't be done with the approach above because it simply regenerates the entire An with each run, and so I cannot differentiate a delta between An and An' due to a user vs that due to a change in function inputs (or code). My initial thought for solving that was to use patch files instead of an array of structs - one set of patches per variant. That is, have the user create a patch that when applied to A, produces An. The function then would simply process each of these patches, and apply those deltas to An' if it exists (or create it by applying the patch to A). I think that would work, but it shifts the burden of variant generation to the user. Perhaps we can have two functions: one that generates the patch, and one that applies it to create An? Patch Generator Function
Behavior:
Variant Generator Function
Behavior:
I think with this approach, re-running the function will not overwrite non-conflicting user edits of An'. Conflicting edits are still an issue though. Worse still, we have issues if we modify the generator function or the set of resources in A, because our patches will now potentially not contain some new resource or value from the modification, so that change will never get added to the generated resources. That is, if we add a resource to such that the selected resources, say A1 now have some additional resources than A, the next run will calculate a patch between A1 and A1n, and the patch won't have the new resource, so it will never be applied to An'. I think we have to take it a step further, and store the patches, along with some sequencing so that we can re-apply them properly ordered. Or maybe even that won't work, but instead we need to store the original An from the previous version of the function. I need to think about it more, but this seems to be getting out-of-hand. Other approaches? |
@johnbelamaric Have you considered modeling this as a set of subpackages? (See go/package-sets.) |
Yes, now that you mention it I did at some point look at that and it looked
promising. I'll have to go back and revisit it. Has anything with respect
to that been implemented?
…On Tue, Dec 14, 2021 at 11:33 AM Morgante Pell ***@***.***> wrote:
@johnbelamaric <https://github.com/johnbelamaric> Have you considered
modeling this as a set of subpackages? (See go/package-sets
<https://goto.google.com/package-sets>.)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2528 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACIHRM666MLI6FOARUU7OPTUQ6LXTANCNFSM5FDSSYCQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
Not yet, the main blocker right now is the challenges with cyclical |
Ah, yes, I handle that in pre-v1.0 kpt with a two-pass pipeline, haven't looked at v1.0 kpt closely enough, hopefully there will be a similar solution available. |
Thinking about this a little more, it does seem that subpackage is the right model. The explicit version in the Kptfile gives us the ancestor for the 3-way merge, so we can make edits via the generator function, via other functions, and via manual user edits of the resulting generated instance. One issue for generators - shouldn't they end up needing to work something like apply, with respect to pruning, for example? If we alter the generator function or its inputs such that you are getting fewer instances of a given subpackage, you'll need to prune the old packages. We could manage this with function-specific logic (for example, "this generator function owns this directory"), but it seems we should have some utility or at least conventions here. |
Yes, though they're actually also often similar to owner references. The approach we've taken thus far is to use an annotation to track the source resource and prune if it's changed. We should definitely standardize this sort of functionality into the SDK though. It's a pain for each function author to write/maintain the logic and implementations might become inconsistent. |
Back to the original topic: Ensure functions make sense to me. That's similar to https://github.com/metacontroller/metacontroller functions. It could be a supported or at least recommended pattern, like variant constructors (#2590). However, my eventual conclusion on expand_team_cr was that it wasn't sufficiently useful for declarative use. Instead, I switched to a minimalistic variant-constructor approach, using the ensure pattern for just the Namespace: #2184 (comment) In order for abstractions need to be worthwhile, they need to dramatically reduce configuration complexity or implement some significant business logic. Otherwise, we should think in terms of manipulating the underlying resource types, with resource-type-specific functions and tools, as imperative tools typically do. I agree that variant generators could be modeled as generated subpackages, which could leverage the variant constructor pattern and specify variant resources declaratively rather than generating them from scratch. We should spawn another issue for that, if there isn't one already. The variant generation function could generate Kptfiles for the subpackages. We'd then need to trigger cloning of the subpackages, and reinvoke the pipeline to run the variant constructors. I am generally not a fan of monolithic arrays or maps as ways to express desired variants. They seem appealing for specifying small numbers of variants with small numbers of varying attributes by hand, but are difficult to manage at non-trivial scale. Probably the set of variants would be derived from some input source using some automation. I'd represent them like other generators, using client-side custom resources, one per variant or per dimensional variation value (e.g., region and environment). Convention over configuration is our friend for these scenarios so that users don't need to specify where the varying attributes come from for each variant. Storing sets of varying attributes or "facts" is common in hierarchical parameter stores. We can use CRs (pseudo or actual) or ConfigMaps for this, which also should make custom UX for such use cases simpler to build. |
For creating individual resources, imperative functions, CLI (e.g. kubectl create -o yaml), UI, etc. seems like the way to go. |
This sets a blueprints.cloud.google.com/ownerReference annotation. Which prunes resources no longer needed. |
Ah, the merge behavior was previously requested in #954 |
The question inevitably comes up regarding what to do when a generator function is updated. It depends on whether changing the behavior of existing uses is desirable or not. It also depends on whether the generator would just support additional optional, parameterized attributes. Helm charts often conditionalize new attributes so that they don't affect existing uses of the chart automatically. Changes of default behavior are also typically undesirable for existing uses. So changing a generator is often not the best way to enforce new behaviors. That said, we may want to support similar behaviors for upgrades to new generator versions similarly to new upstream package versions. One way to do that would be to update the function version in upstream packages first, then update their downstream packages. We'd want to surface available function upgrades similar to available package upgrades. This would be easier if we separated upstream and downstream functions (#2544) as well as generators and admission control (#3121). |
One quick thought -- I can imagine generator function to represent a subset of resources of the package (sort of a slice of the package) sourced by running a specific version of function similar to |
The issue of pruning also applies to transformers that change resource identifiers, in the case that the identifier transformation is changed across revisions. |
Drive-by observation: Infrastructure from Code projects that generate Infrastructure as Code formats are going to have similar challenges. Users will want to customize the generated IaC code/templates, but those changes will be stomped the next time the IaC is regenerated. The idea of generating app config from app code is not a terrible one. "oc new-app" is a simple version of that. |
Something of interest here is what @henderiw and others are doing in Nephio. There, we are using functions to generate resources, as well as using the package conditions function of Porch. This means we need to operate a lot like a controller; we need to create, update, and prune child resources based upon one or more inputs. So, they are working on a controller-runtime like function SDK that handles various aspects of that for the function authors. Wim - can you link in your slides / maybe the PR? This is very early work, but in a month or so it may be good to discuss in kpt office hours. |
Attached the link to the slides. https://docs.google.com/presentation/d/1xKZM4Q_auoUMb6M4I_OTNlS7xu7K6cVm2F_FQg217lU/edit?usp=sharing |
I've concluded that generators need special treatment.
Preprocessors (#2420) are ok, but are a workaround. #2466 is one issue that has been reported, but there are more issues.
Let's look at a generator like https://github.com/GoogleContainerTools/kpt-functions-sdk/blob/master/ts/demo-functions/src/expand_team_cr.ts.
cc @justinsb
The text was updated successfully, but these errors were encountered: