Repository navigation
✨ 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 |
|---|---|---|
|
|
@@ -99,8 +99,10 @@ 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. | ||
| // <opcon:experimental:description> | ||
| // Setting sourceType to "OCIImage" requires the ociImage field to also be defined. | ||
| // </opcon:experimental:description> | ||
| // | ||
| // Below is a minimal example of a source definition (in yaml): | ||
| // | ||
|
|
@@ -142,31 +144,110 @@ 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' ? has(self.ociImage) : !has(self.ociImage)",message="ociImage is required when sourceType is OCIImage, and forbidden otherwise"> | ||
| type SourceConfig struct { | ||
| // sourceType is required and specifies the type of install source. | ||
| // | ||
| // <opcon:standard:description> | ||
| // The only 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"` | ||
|
|
||
| // catalog configures how information is sourced from a catalog. | ||
| // It is required when sourceType is "Catalog", and forbidden otherwise. | ||
| // | ||
| // +optional | ||
| Catalog *CatalogFilter `json:"catalog,omitempty"` | ||
| Catalog CatalogFilter `json:"catalog,omitzero"` | ||
|
|
||
| // ociImage configures a bundle image to install directly. | ||
| // <opcon:experimental:description> | ||
|
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. The whole field is experimental. So I assume these extra experimental description markers are serving no purpose? Match the pattern we use for the description of the catalog field as well. Specifically, the part about required vs forbidden. |
||
| // They do not provide catalog dependency resolution or upgrade safety. | ||
|
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. Catalogs ALSO don't provide dependency resolution so I'd drop that part. Also nit: update safety is a field only on the catalog source spec, so it may be that it is implied that DBI does not support update graph safety because that is not a field on the DBI source spec? Overall point is mainly that if we had a larger enum space, I don't think we'd be comparing/contrasting all the source types in their descriptions. |
||
| // </opcon:experimental:description> | ||
| // <opcon:experimental> | ||
| // +optional | ||
| OCIImage OCIImageSource `json:"ociImage,omitzero"` | ||
|
joelanford marked this conversation as resolved.
|
||
| } | ||
|
|
||
| // OCIImageSource identifies a bundle image to install directly from an OCI registry. | ||
| // +kubebuilder:validation:MinProperties:=1 | ||
| type OCIImageSource struct { | ||
| // ref is a required field that defines the reference to a container image for a registry+v1 bundle. | ||
| // It cannot be more than 1000 characters. | ||
| // | ||
| // A reference has 3 parts: the domain, name, and identifier. | ||
| // | ||
| // The domain is typically the registry where an image is located. | ||
| // It must be alphanumeric characters (lowercase and uppercase) separated by the "." character. | ||
| // Hyphenation is allowed, but the domain must start and end with alphanumeric characters. | ||
| // Specifying a port to use is also allowed by adding the ":" character followed by numeric values. | ||
| // The port must be the last value in the domain. | ||
| // Some examples of valid domain values are "registry.mydomain.io", "quay.io", "my-registry.io:8080". | ||
| // | ||
| // The name is typically the repository in the registry where an image is located. | ||
| // It must contain lowercase alphanumeric characters separated only by the ".", "_", "__", "-" characters. | ||
| // Multiple names can be concatenated with the "/" character. | ||
| // The domain and name are combined using the "/" character. | ||
| // Some examples of valid name values are "operatorhubio/catalog", "catalog", "my-catalog.prod". | ||
| // An example of the domain and name parts of a reference being combined is "quay.io/operatorhubio/catalog". | ||
| // | ||
| // The identifier is typically the tag or digest for an image reference and is present at the end of the reference. | ||
| // It starts with a separator character used to distinguish the end of the name and beginning of the identifier. | ||
| // For a digest-based reference, the "@" character is the separator. | ||
| // For a tag-based reference, the ":" character is the separator. | ||
| // An identifier is required in the reference. | ||
| // | ||
| // Digest-based references must contain an algorithm reference immediately after the "@" separator. | ||
| // The algorithm reference must be followed by the ":" character and an encoded string. | ||
| // The algorithm must start with an uppercase or lowercase alpha character followed by alphanumeric characters and may contain the "-", "_", "+", and "." characters. | ||
| // Some examples of valid algorithm values are "sha256", "sha256+b64u", "multihash+base58". | ||
| // The encoded string following the algorithm must be hex digits (a-f, A-F, 0-9) and must be a minimum of 32 characters. | ||
| // | ||
| // Tag-based references must begin with a word character (alphanumeric + "_") followed by word characters or ".", and "-" characters. | ||
| // The tag must not be longer than 127 characters. | ||
| // | ||
| // An example of a valid digest-based image reference is "quay.io/operatorhubio/catalog@sha256:200d4ddb2a73594b91358fe6397424e975205bfbe44614f5846033cad64b3f05" | ||
| // An example of a valid tag-based image reference is "quay.io/operatorhubio/catalog:latest" | ||
| // | ||
| // +required | ||
| // +kubebuilder:validation:MaxLength:=1000 | ||
| // +kubebuilder:validation:XValidation:rule="self.matches('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])((\\\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(:[0-9]+)?\\\\b')",message="must start with a valid domain. valid domains must be alphanumeric characters (lowercase and uppercase) separated by the \".\" character." | ||
| // +kubebuilder:validation:XValidation:rule="self.find('(\\\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((\\\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)') != \"\"",message="a valid name is required. valid names must contain lowercase alphanumeric characters separated only by the \".\", \"_\", \"__\", \"-\" characters." | ||
| // +kubebuilder:validation:XValidation:rule="self.find('(@.*:)') != \"\" || self.find(':.*$') != \"\"",message="must end with a digest or a tag" | ||
| // +kubebuilder:validation:XValidation:rule="self.find('(@.*:)') == \"\" ? (self.find(':.*$') != \"\" ? self.find(':.*$').substring(1).size() <= 127 : true) : true",message="tag is invalid. the tag must not be more than 127 characters" | ||
| // +kubebuilder:validation:XValidation:rule="self.find('(@.*:)') == \"\" ? (self.find(':.*$') != \"\" ? self.find(':.*$').matches(':[\\\\w][\\\\w.-]*$') : true) : true",message="tag is invalid. valid tags must begin with a word character (alphanumeric + \"_\") followed by word characters or \".\", and \"-\" characters" | ||
| // +kubebuilder:validation:XValidation:rule="self.find('(@.*:)') != \"\" ? self.find('(@.*:)').matches('(@[A-Za-z][A-Za-z0-9]*([-_+.][A-Za-z][A-Za-z0-9]*)*[:])') : true",message="digest algorithm is not valid. valid algorithms must start with an uppercase or lowercase alpha character followed by alphanumeric characters and may contain the \"-\", \"_\", \"+\", and \".\" characters." | ||
| // +kubebuilder:validation:XValidation:rule="self.find('(@.*:)') != \"\" ? self.find(':.*$').substring(1).size() >= 32 : true",message="digest is not valid. the encoded string must be at least 32 characters" | ||
| // +kubebuilder:validation:XValidation:rule="self.find('(@.*:)') != \"\" ? self.find(':.*$').matches(':[0-9A-Fa-f]*$') : true",message="digest is not valid. the encoded string must only contain hex characters (A-F, a-f, 0-9)" | ||
|
Comment on lines
+244
to
+249
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. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Parse the tag and digest after the repository path, not from the first The tag and digest rules extract the identifier with
Both port cases in The name rule on Line 240 has a separate gap. It uses an unanchored Derive the identifier after the last 🤖 Prompt for AI Agents
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 don't disagree, but I think that since this is the same criteria as ClusterCatalog we want to address any pullspec validation correction in both places at the same time, and I'd prefer to break that out in its own item 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.
Would you like me to open a GitHub issue for that work? ✏️ Learnings added
You are interacting with an AI system. |
||
| Ref string `json:"ref"` | ||
| } | ||
|
|
||
| // 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.
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.
Install or update to?