-
Notifications
You must be signed in to change notification settings - Fork 84
✨ implementation of a source-sniffing 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,30 @@ 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" | ||
| // +kubebuilder: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. | ||
| // | ||
| // The only allowed value is "Catalog". | ||
| // 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. | ||
| // | ||
| // +unionDiscriminator | ||
| // +kubebuilder:validation:Enum:="Catalog" | ||
| // +kubebuilder:validation:Enum:="Catalog";"OCIImage" | ||
| // +required | ||
| SourceType string `json:"sourceType"` | ||
|
|
||
|
|
@@ -147,6 +153,29 @@ type SourceConfig struct { | |
| // | ||
| // +optional | ||
| Catalog *CatalogFilter `json:"catalog,omitempty"` | ||
|
|
||
| // ociImage configures a bundle image to install directly. | ||
| // They do not provide catalog dependency resolution or upgrade safety. | ||
| // | ||
| // +optional | ||
| OCIImage *OCIImageSource `json:"ociImage,omitempty"` | ||
|
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. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🤖 get_repo_knowledge executed:
Length of output: 4666 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- api/v1/clusterextension_types.go ---'
cat -n api/v1/clusterextension_types.go | sed -n '110,190p'
printf '%s\n' '--- related declarations and uses ---'
rg -n -C 3 'type OCIImageSource|OCIImageSource|OCIImage\b|SourceTypeOCIImage|SourceConfig' api/v1 config internal test 2>/dev/null | head -240
printf '%s\n' '--- relevant tracked generated files ---'
git ls-files | rg '(^|/)(zz_generated.deepcopy.go|.*(crd|manifest|apply|reference).*)$' | head -160Repository: operator-framework/operator-controller Length of output: 40535 🏁 Script executed: #!/bin/bash
set -e
cat -n api/v1/clusterextension_types.go | sed -n '130,180p'
printf '%s\n' '--- declarations ---'
rg -n -C 5 'type OCIImageSource|OCIImageSource|OCIImage\b|SourceTypeOCIImage' api/v1Repository: operator-framework/operator-controller Length of output: 10030 Use a value field for optional Declare it as 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| } | ||
|
|
||
| // OCIImageSource identifies a bundle image to install directly from an OCI registry. | ||
| type OCIImageSource struct { | ||
| // ref is a Docker-style image reference with a tag or digest. | ||
| // | ||
| // +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" | ||
| // +kubebuilder:validation:XValidation:rule="self.find(\"(\\\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?((\\\\/[a-z0-9]+((([._]|__|[-]*)[a-z0-9]+)+)?)+)?)\") != \"\"",message="a valid image name is required" | ||
| // +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" | ||
| // +kubebuilder:validation:XValidation:rule="self.find(\"(@.*:)\") == \"\" ? (self.find(\":.*$\") != \"\" ? self.find(\":.*$\").matches(\":[\\\\w][\\\\w.-]*$\") : true) : true",message="tag is invalid" | ||
| // +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" | ||
|
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 | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- changed file context ---'
sed -n '130,205p' api/v1/clusterextension_types.go
printf '%s\n' '--- related OCI validation and tests ---'
rg -n -C 4 'OCIImage|find\\\(":\\.\\*\\$"|digest algorithm|registry\\.example|port-qualified|oci' api/v1 config test 2>/dev/null || trueRepository: operator-framework/operator-controller Length of output: 50395 🤖 get_repo_knowledge executed:
Length of output: 3603 Handle registry ports before validating the tag or digest.
🤖 Prompt for AI Agents |
||
| // +kubebuilder:validation:XValidation:rule="self.find(\"(@.*:)\") != \"\" ? self.find(\":.*$\").substring(1).size() >= 32 : true",message="digest is not valid" | ||
| // +kubebuilder:validation:XValidation:rule="self.find(\"(@.*:)\") != \"\" ? self.find(\":.*$\").matches(\":[0-9A-Fa-f]*$\") : true",message="digest is not valid" | ||
| 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.
Separate enum validation and the type addition between standard and experimental, such that only experimental adds the support for the new enum/type since this appears to be behind the experimental only
BoxcutterRuntimefeature gate?