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

✨ WIP: DO NOT MERGE: Shift Week Prototypes #1418

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,11 @@ tidy: #HELP Update dependencies.
manifests: $(CONTROLLER_GEN) #EXHELP Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/base/crd/bases output:rbac:artifacts:config=config/base/rbac

OPENAPI_VERSION := $(shell go list -m k8s.io/api | cut -d" " -f2 | sed 's/^v0/v1/')
.PHONY: generate
generate: $(CONTROLLER_GEN) #EXHELP Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
curl -sSL https://raw.githubusercontent.com/kubernetes/kubernetes/refs/tags/$(OPENAPI_VERSION)/api/openapi-spec/v3/apis__apps__v1_openapi.json > ./internal/rukpak/convert/v2/internal/apis__apps__v1_openapi.json

.PHONY: verify
verify: tidy fmt vet generate manifests crd-ref-docs #HELP Verify all generated code is up-to-date.
Expand Down
193 changes: 189 additions & 4 deletions api/v1/clusterextension_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1

import (
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down Expand Up @@ -87,34 +88,57 @@ type ClusterExtensionSpec struct {
// +kubebuilder:validation:Required
Source SourceConfig `json:"source"`

// config stores any custom configuration to be used when templating
// content for this extension.
//
// config is optional. When not specified, the package manager will use
// the default configuration of the extension.
//
//+optional
Template *ClusterExtensionTemplate `json:"template,omitempty"`

// install is an optional field used to configure the installation options
// for the ClusterExtension such as the pre-flight check configuration.
//
// +optional
Install *ClusterExtensionInstallConfig `json:"install,omitempty"`
}

const SourceTypeCatalog = "Catalog"
const (
SourceTypeBundle = "Bundle"
SourceTypeCatalog = "Catalog"
)

// SourceConfig is a discriminated union which selects the installation source.
//
// +union
// +kubebuilder:validation:XValidation:rule="has(self.sourceType) && self.sourceType == 'Bundle' ?has(self.bundle) : !has(self.bundle)",message="bundle is required when sourceType is Bundle, and forbidden otherwise"
// +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"
type SourceConfig struct {
// sourceType is a required reference to the type of install source.
//
// Allowed values are "Catalog"
// Allowed values are "Bundle" or "Catalog"
//
// When this field is set to "Bundle", the bundle of content to install is specified
// directly. In this case, no interaction with ClusterCatalog resources is necessary.
// When using the Bundle sourceType, the bundle field must also be set.
//
// When this field is set to "Catalog", information for determining the
// appropriate bundle of content to install will be fetched from
// ClusterCatalog resources existing on the cluster.
// When using the Catalog sourceType, the catalog field must also be set.
//
// +unionDiscriminator
// +kubebuilder:validation:Enum:="Catalog"
// +kubebuilder:validation:Enum:=Bundle;Catalog
// +kubebuilder:validation:Required
SourceType string `json:"sourceType"`

// bundle is used to configure how information is sourced from a bundle.
// This field is required when sourceType is "Bundle", and forbidden otherwise.
//
// +optional.
Bundle *BundleSource `json:"bundle,omitempty"`

// catalog is used to configure how information is sourced from a catalog.
// This field is required when sourceType is "Catalog", and forbidden otherwise.
//
Expand All @@ -138,6 +162,113 @@ type ClusterExtensionInstallConfig struct {
Preflight *PreflightConfig `json:"preflight,omitempty"`
}

type ValuesSourceType string

const (
ValuesSourceTypeInline ValuesSourceType = "Inline"
ValuesSourceTypeConfigMap ValuesSourceType = "ConfigMap"
ValuesSourceTypeSecret ValuesSourceType = "Secret"
)

type ClusterExtensionTemplate struct {
// valuesSources is a list of sources from which to obtain arbitrary values that
// provide configuration for the installation of bundles managed by the
// ClusterExtension.
//
// valuesSources is optional. When not specified, the package manager will use
// the default configuration of the resolved bundle.
//
// If multiple valuesSources are specified, the values are merged in the order
// they are specified. Values from later sources will override values from earlier
// sources.
//
// Bundles can optionally provide a schema for these values. When bundles provide
// a schema, it is used to validate these values before proceeding with the
// installation. Validation errors are reported via the ClusterExtension status.
//
//+optional
ValuesSources []ValuesSource `json:"valuesSources,omitempty"`
}

// ValuesSource is a discriminated union of possible sources for values.
// ValuesSource contains the sourcing information for those values.
// +union
// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Inline' ?has(self.inline) : !has(self.inline)",message="inline is required when type is Inline, and forbidden otherwise"
// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'ConfigMap' ?has(self.configMap) : !has(self.configMap)",message="configMap is required when type is ConfigMap, and forbidden otherwise"
// +kubebuilder:validation:XValidation:rule="has(self.type) && self.type == 'Secret' ?has(self.secret) : !has(self.secret)",message="secret is required when type is Secret, and forbidden otherwise"
type ValuesSource struct {
// type is a reference to the type of source the values are sourced from.
// type is required.
//
// The allowed values are "Inline", "ConfigMap", and "Secret".
//
// When set to "Inline", the values are sourced directly from the inlined content.
// When using an inline source, the inline field must be set and must be the only field defined for this type.
//
// When set to "ConfigMap", the values are sourced from the specified ConfigMap in the installNamespace.
// When using a ConfigMap source, the configMap field must be set and must be the only field defined for this type.
//
// When set to "Secret", the values are sourced from the specified Secret in the installNamespace.
// When using a Secret source, the secret field must be set and must be the only field defined for this type.
//
// +unionDiscriminator
// +kubebuilder:validation:Enum:="Inline";"ConfigMap";"Secret"
// +kubebuilder:validation:Required
Type ValuesSourceType `json:"type"`

// inline is a map of arbitrary key-value pairs.
//
// Inlined values are useful for small, simple configurations that do not
// include sensitive information.
//
//+kubebuilder:pruning:PreserveUnknownFields
//+kubebuilder:validation:Type=object
//+kubebuilder:validation:Schemaless
//+optional
Inline *apiextensionsv1.JSON `json:"inline,omitempty"`

// configMap is a reference to a key in a specific ConfigMap in the installNamespace.
// The referenced ConfigMap is expected to contain the specified key, whose value
// contains the desired configuration.
//
// ConfigMaps are useful for storing larger, more complex configurations that do
// not include sensitive information.
//
// The service account provided in the spec.install field must have 'get' permission in
// order to read the referenced ConfigMap.
//
//+optional
ConfigMap *LocalObjectReferenceWithKey `json:"configMap,omitempty"`

// secret is a reference to a key in a specific Secret in the installNamespace.
// The referenced Secret is expected to contain the specified key, whose value
// contains the desired configuration.
//
// Secrets are useful for storing larger, more complex configurations or
// configurations that include sensitive information.
//
// The service account provided in the spec.install field must have 'get' permission in
// order to read the referenced Secret.
//
//+optional
Secret *LocalObjectReferenceWithKey `json:"secret,omitempty"`
}

type LocalObjectReferenceWithKey struct {
// name is the name of a resource in the same namespace as the ClusterExtension.
// name is required.
//
//+kubebuilder:validation:Required
Name string `json:"name"`

// key is a reference to a key in the data field of
// the referenced object.
// key is required.
//
//+kubebuilder:validation:Required
Key string `json:"key"`
}

// CatalogSource defines the attributes used to identify and filter content from a catalog.
type CatalogSource struct {
// packageName is a reference to the name of the package to be installed
Expand Down Expand Up @@ -327,7 +458,61 @@ type CatalogSource struct {
UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"`
}

// ServiceAccountReference identifies the serviceAccount used fo install a ClusterExtension.
// BundleSource defines the configuration used to retrieve a bundle directly from
// its OCI-based image reference.
type BundleSource struct {
// ref allows users to define the reference to a container image containing bundle contents.
// ref is required.
// ref can not be more than 1000 characters.
//
// A reference can be broken down into 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/bundle", "bundle", "my-bundle.prod".
// An example of the domain and name parts of a reference being combined is "quay.io/operatorhubio/bundle".
//
// 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"
//
// +kubebuilder:validation: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)"
Ref string `json:"ref"`
}

// ServiceAccountReference identifies the serviceAccount used to install a ClusterExtension.
type ServiceAccountReference struct {
// name is a required, immutable reference to the name of the ServiceAccount
// to be used for installation and management of the content for the package
Expand Down
93 changes: 93 additions & 0 deletions api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading