-
Notifications
You must be signed in to change notification settings - Fork 85
✨ implementation of a registry+v1 direct bundle installer #2907
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -79,7 +79,6 @@ type ClusterExtensionSpec struct { | |
| // source is required and selects the installation source of content for this ClusterExtension. | ||
| // Set the sourceType field to perform the selection. | ||
| // | ||
| // Catalog is currently the only implemented sourceType. | ||
| // Setting sourceType to "Catalog" requires the catalog field to also be defined. | ||
| // | ||
| // Below is a minimal example of a source definition (in yaml): | ||
|
|
@@ -122,23 +121,41 @@ type ClusterExtensionSpec struct { | |
| ProgressDeadlineMinutes int32 `json:"progressDeadlineMinutes,omitempty"` | ||
| } | ||
|
|
||
| const SourceTypeCatalog = "Catalog" | ||
| const ( | ||
| SourceTypeCatalog = "Catalog" | ||
| SourceTypeOCIImage = "OCIImage" | ||
| ) | ||
|
|
||
| // SourceConfig is a discriminated union which selects the installation source. | ||
| // | ||
| // +union | ||
| // +kubebuilder:validation:XValidation:rule="has(self.sourceType) && self.sourceType == 'Catalog' ? has(self.catalog) : !has(self.catalog)",message="catalog is required when sourceType is Catalog, and forbidden otherwise" | ||
| // <opcon:experimental:validation:XValidation:rule="has(self.sourceType) && self.sourceType == 'OCIImage' ? self.ociImage.ref.size() != 0 : self.ociImage.ref.size() == 0",message="ociImage is required when sourceType is OCIImage, and forbidden otherwise"> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If feels like
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The check does this, but instead of ">0" it's "!=0". This is just looking at the first field in the first field.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this is mainly a separation of concerns comment.
|
||
| type SourceConfig struct { | ||
| // sourceType is required and specifies the type of install source. | ||
| // | ||
| // The only allowed value is "Catalog". | ||
| // <opcon:standard:description> | ||
| // The allowed value is "Catalog". | ||
| // | ||
| // When set to "Catalog", information for determining the appropriate bundle of content to install | ||
| // is fetched from ClusterCatalog resources on the cluster. | ||
| // When using the Catalog sourceType, the catalog field must also be set. | ||
| // </opcon:standard:description> | ||
| // | ||
| // <opcon:experimental:description> | ||
| // The allowed values are "Catalog" and "OCIImage". | ||
| // | ||
| // When set to "OCIImage", the bundle image is used directly. Direct sources do not perform | ||
| // dependency resolution and are only supported by the Boxcutter runtime. | ||
| // | ||
| // When set to "Catalog", information for determining the appropriate bundle of content to install | ||
| // is fetched from ClusterCatalog resources on the cluster. | ||
| // When using the Catalog sourceType, the catalog field must also be set. | ||
| // </opcon:experimental:description> | ||
| // | ||
| // +unionDiscriminator | ||
| // +kubebuilder:validation:Enum:="Catalog" | ||
| // <opcon:experimental:validation:Enum=Catalog;OCIImage> | ||
| // +required | ||
| SourceType string `json:"sourceType"` | ||
|
|
||
|
|
@@ -147,6 +164,26 @@ type SourceConfig struct { | |
| // | ||
| // +optional | ||
| Catalog *CatalogFilter `json:"catalog,omitempty"` | ||
|
|
||
| // ociImage configures a bundle image to install directly. | ||
| // <opcon:experimental:description> | ||
| // They do not provide catalog dependency resolution or upgrade safety. | ||
| // </opcon:experimental:description> | ||
| // <opcon:experimental> | ||
| // +optional | ||
| OCIImage OCIImageSource `json:"ociImage,omitzero"` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we make this a pointer with I know Another thing I think we are free to do is change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I originally did, and coderabbit flagged for omitzero instead.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ehh, if we have API level validation that says "if sourceType is OCIImage, then Also, I'd go out on a (short?) limb and say "coderabbit is wrong" to suggest that we follow a different type pattern among different union members. I feel like the only right answers are:
I think we'd see go-apidiff fail but no mention in crddiff (I don't think the CRD schema would actually change, but I might be wrong). |
||
| } | ||
|
|
||
| // OCIImageSource identifies a bundle image to install directly from an OCI registry. | ||
| // +kubebuilder:validation:MinProperties:=1 | ||
| type OCIImageSource struct { | ||
| // ref is a Docker-style image reference with a tag or digest. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the ref uses a tag, what is our behavior when the tag is moved to a different digest in the image registry? And then the follow-up question would be: is that the behavior that users would expect/that we want to support? We should document that behavior and test for it. Alternatively, we could require a digest at least to start. I know the UX of that is worse, but it is simpler for us to deal with and easier for readers of this API to reason about. |
||
| // | ||
| // +required | ||
| // +kubebuilder:validation:MaxLength:=1000 | ||
| // +kubebuilder:validation:MinLength:=1 | ||
| // +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?(:[0-9]+)?/[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*(:[A-Za-z0-9_][A-Za-z0-9_.-]{0,126}|@[A-Za-z][A-Za-z0-9+._-]*:[0-9A-Fa-f]{32,})$\")",message="must be a complete image reference with a valid repository and tag or digest" | ||
|
Comment on lines
+182
to
+185
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just checking that this is the exact same validation that we are using for the format of the catalogd image source? I know we have an extra check there for "digest disallowed with poll interval", but other than that, I'd expect to duplicate the validation.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was a minimal lift-and-shift from earlier work. I've adopted the more-robust catalogsource ref validation here now. |
||
| Ref string `json:"ref,omitempty"` | ||
| } | ||
|
|
||
| // ClusterExtensionInstallConfig is a union which selects the clusterExtension installation config. | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -434,7 +434,7 @@ func run() error { | |
| return catalogclient.BuildHTTPClient(cpwCatalogd) | ||
| }) | ||
|
|
||
| resolver := &resolve.CatalogResolver{ | ||
| catalogResolver := &resolve.CatalogResolver{ | ||
| WalkCatalogsFunc: resolve.CatalogWalker( | ||
| func(ctx context.Context, option ...client.ListOption) ([]ocv1.ClusterCatalog, error) { | ||
| var catalogs ocv1.ClusterCatalogList | ||
|
|
@@ -449,6 +449,15 @@ func run() error { | |
| resolve.NoDependencyValidation, | ||
| }, | ||
| } | ||
| resolver := resolve.MultiResolver{ | ||
| ocv1.SourceTypeCatalog: catalogResolver, | ||
| } | ||
| if features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime) { | ||
| resolver.RegisterType(ocv1.SourceTypeOCIImage, &resolve.OCIImageResolver{ | ||
| Puller: imagePuller, | ||
| Cache: imageCache, | ||
| }) | ||
| } | ||
|
|
||
| aeClient, err := apiextensionsv1client.NewForConfig(mgr.GetConfig()) | ||
| if err != nil { | ||
|
|
@@ -654,6 +663,8 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl | |
| controllers.HandleFinalizers(c.finalizers), | ||
| controllers.ValidateClusterExtension( | ||
| controllers.ServiceAccountDeprecationWarning(), | ||
| controllers.DirectBundleRequiresBoxcutter(), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: I wonder if we should combine the functionality of |
||
| controllers.ValidateDirectBundleSource(), | ||
| ), | ||
| controllers.MigrateStorage(storageMigrator), | ||
| controllers.RetrieveRevisionStates(revisionStatesGetter), | ||
|
|
@@ -742,6 +753,8 @@ func (c *helmReconcilerConfigurator) Configure(ceReconciler *controllers.Cluster | |
| controllers.HandleFinalizers(c.finalizers), | ||
| controllers.ValidateClusterExtension( | ||
| controllers.ServiceAccountDeprecationWarning(), | ||
| controllers.DirectBundleRequiresBoxcutter(), | ||
| controllers.ValidateDirectBundleSource(), | ||
| ), | ||
| controllers.RetrieveRevisionStates(revisionStatesGetter), | ||
| controllers.ResolveBundle(c.resolver, c.mgr.GetClient()), | ||
|
|
||
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.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Guard
ociImagein the experimental validation marker.hack/tools/crd-generatorprocesses this type-level marker when it transforms the generatedsourceschema. WhenociImageis absent for aCatalogsource, the current rule evaluatesself.ociImage.refand can reject the valid source. Use:Add admission coverage for both missing-field cases, then run
make generate manifests crd-ref-docs lint-api-diff.🤖 Prompt for AI Agents