diff --git a/.github/workflows/pull-requests.yaml b/.github/workflows/pull-requests.yaml index 06b4f3d3..2de01160 100644 --- a/.github/workflows/pull-requests.yaml +++ b/.github/workflows/pull-requests.yaml @@ -58,7 +58,7 @@ jobs: DOCKER_CONFIG: ${{ runner.temp }}/.docker - name: Build Talos image - run: make -C packages/core/installer talos-nocloud + run: make -C packages/core/talos talos-nocloud - name: Save git diff as patch if: "!contains(github.event.pull_request.labels.*.name, 'release')" diff --git a/Makefile b/Makefile index 2038ff9f..e3c9b62b 100644 --- a/Makefile +++ b/Makefile @@ -26,21 +26,18 @@ build: build-deps make -C packages/system/bucket image make -C packages/system/objectstorage-controller image make -C packages/core/testing image - make -C packages/core/installer image + make -C packages/core/installer image-operator + make -C packages/core/talos image + make -C packages/core/platform image + make -C packages/core/installer image-packages make manifests -repos: - rm -rf _out - make -C packages/system repo - make -C packages/apps repo - make -C packages/extra repo - manifests: mkdir -p _out/assets - (cd packages/core/installer/; helm template -n cozy-installer installer .) > _out/assets/cozystack-installer.yaml + (cd packages/core/installer/; helm template -n cozy-system cozystack-operator . | sed '/^WARNING/d') > _out/assets/cozystack-installer.yaml assets: - make -C packages/core/installer assets + make -C packages/core/talos assets test: make -C packages/core/testing apply diff --git a/api/v1alpha1/cozystackresourcedefinitions_types.go b/api/v1alpha1/applicationdefinitions_types.go similarity index 69% rename from api/v1alpha1/cozystackresourcedefinitions_types.go rename to api/v1alpha1/applicationdefinitions_types.go index b2baee85..c8b08795 100644 --- a/api/v1alpha1/cozystackresourcedefinitions_types.go +++ b/api/v1alpha1/applicationdefinitions_types.go @@ -18,50 +18,51 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) // +kubebuilder:object:root=true -// +kubebuilder:resource:scope=Cluster +// +kubebuilder:resource:scope=Cluster,shortName=appdef -// CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions API -type CozystackResourceDefinition struct { +// ApplicationDefinition is the Schema for the applicationdefinitions API +type ApplicationDefinition struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec CozystackResourceDefinitionSpec `json:"spec,omitempty"` + Spec ApplicationDefinitionSpec `json:"spec,omitempty"` } // +kubebuilder:object:root=true -// CozystackResourceDefinitionList contains a list of CozystackResourceDefinitions -type CozystackResourceDefinitionList struct { +// ApplicationDefinitionList contains a list of ApplicationDefinitions +type ApplicationDefinitionList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []CozystackResourceDefinition `json:"items"` + Items []ApplicationDefinition `json:"items"` } func init() { - SchemeBuilder.Register(&CozystackResourceDefinition{}, &CozystackResourceDefinitionList{}) + SchemeBuilder.Register(&ApplicationDefinition{}, &ApplicationDefinitionList{}) } -type CozystackResourceDefinitionSpec struct { +type ApplicationDefinitionSpec struct { // Application configuration - Application CozystackResourceDefinitionApplication `json:"application"` + Application ApplicationDefinitionApplication `json:"application"` // Release configuration - Release CozystackResourceDefinitionRelease `json:"release"` + Release ApplicationDefinitionRelease `json:"release"` // Secret selectors - Secrets CozystackResourceDefinitionResources `json:"secrets,omitempty"` + Secrets ApplicationDefinitionResources `json:"secrets,omitempty"` // Service selectors - Services CozystackResourceDefinitionResources `json:"services,omitempty"` + Services ApplicationDefinitionResources `json:"services,omitempty"` // Ingress selectors - Ingresses CozystackResourceDefinitionResources `json:"ingresses,omitempty"` + Ingresses ApplicationDefinitionResources `json:"ingresses,omitempty"` // Dashboard configuration for this resource - Dashboard *CozystackResourceDefinitionDashboard `json:"dashboard,omitempty"` + Dashboard *ApplicationDefinitionDashboard `json:"dashboard,omitempty"` } -type CozystackResourceDefinitionChart struct { +type ApplicationDefinitionChart struct { // Name of the Helm chart Name string `json:"name"` // Source reference for the Helm chart @@ -79,7 +80,7 @@ type SourceRef struct { Namespace string `json:"namespace"` } -type CozystackResourceDefinitionApplication struct { +type ApplicationDefinitionApplication struct { // Kind of the application, used for UI and API Kind string `json:"kind"` // OpenAPI schema for the application, used for API validation @@ -90,16 +91,30 @@ type CozystackResourceDefinitionApplication struct { Singular string `json:"singular"` } -type CozystackResourceDefinitionRelease struct { - // Helm chart configuration - Chart CozystackResourceDefinitionChart `json:"chart"` +// +kubebuilder:validation:XValidation:rule="(has(self.chart) && !has(self.chartRef)) || (!has(self.chart) && has(self.chartRef))",message="either chart or chartRef must be set, but not both" +type ApplicationDefinitionRelease struct { + // Helm chart configuration (for HelmRepository source) + // +optional + Chart *ApplicationDefinitionChart `json:"chart,omitempty"` + // Chart reference configuration (for ExternalArtifact source) + // +optional + ChartRef *ApplicationDefinitionChartRef `json:"chartRef,omitempty"` // Labels for the release Labels map[string]string `json:"labels,omitempty"` // Prefix for the release name Prefix string `json:"prefix"` + // Default values to be merged into every HelmRelease created from this resource definition + // User-specified values in Application spec will override these default values + // +optional + Values *apiextensionsv1.JSON `json:"values,omitempty"` } -// CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. +type ApplicationDefinitionChartRef struct { + // Source reference for the chart (ExternalArtifact) + SourceRef SourceRef `json:"sourceRef"` +} + +// ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. // A resource matches this selector only if it satisfies ALL criteria: // - Label selector conditions (matchExpressions and matchLabels) // - AND has a name that matches one of the names in resourceNames (if specified) @@ -121,7 +136,7 @@ type CozystackResourceDefinitionRelease struct { // - "{{ .name }}-secret" // - "{{ .kind }}-{{ .name }}-tls" // - "specificname" -type CozystackResourceDefinitionResourceSelector struct { +type ApplicationDefinitionResourceSelector struct { metav1.LabelSelector `json:",inline"` // ResourceNames is a list of resource names to match // If specified, the resource must have one of these exact names to match the selector @@ -129,16 +144,16 @@ type CozystackResourceDefinitionResourceSelector struct { ResourceNames []string `json:"resourceNames,omitempty"` } -type CozystackResourceDefinitionResources struct { +type ApplicationDefinitionResources struct { // Exclude contains an array of resource selectors that target resources. // If a resource matches the selector in any of the elements in the array, it is // hidden from the user, regardless of the matches in the include array. - Exclude []*CozystackResourceDefinitionResourceSelector `json:"exclude,omitempty"` + Exclude []*ApplicationDefinitionResourceSelector `json:"exclude,omitempty"` // Include contains an array of resource selectors that target resources. // If a resource matches the selector in any of the elements in the array, and // matches none of the selectors in the exclude array that resource is marked // as a tenant resource and is visible to users. - Include []*CozystackResourceDefinitionResourceSelector `json:"include,omitempty"` + Include []*ApplicationDefinitionResourceSelector `json:"include,omitempty"` } // ---- Dashboard types ---- @@ -155,8 +170,8 @@ const ( DashboardTabYAML DashboardTab = "yaml" ) -// CozystackResourceDefinitionDashboard describes how this resource appears in the UI. -type CozystackResourceDefinitionDashboard struct { +// ApplicationDefinitionDashboard describes how this resource appears in the UI. +type ApplicationDefinitionDashboard struct { // Human-readable name shown in the UI (e.g., "Bucket") Singular string `json:"singular"` // Plural human-readable name (e.g., "Buckets") diff --git a/api/v1alpha1/bundles_types.go b/api/v1alpha1/bundles_types.go new file mode 100644 index 00000000..3dd6542c --- /dev/null +++ b/api/v1alpha1/bundles_types.go @@ -0,0 +1,230 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,shortName=bundle + +// Bundle is the Schema for the bundles API +type Bundle struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BundleSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true + +// BundleList contains a list of Bundles +type BundleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Bundle `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Bundle{}, &BundleList{}) +} + +// BundleSpec defines the desired state of Bundle +type BundleSpec struct { + // SourceRef is the source reference for the bundle charts + // +required + SourceRef BundleSourceRef `json:"sourceRef"` + + // DependsOn is a list of bundle dependencies in the format "bundleName/target" + // For example: "cozystack-system/network" + // If specified, the dependencies listed in the target's packages will be taken + // from the specified bundle and added to all packages in this bundle + // +optional + DependsOn []string `json:"dependsOn,omitempty"` + + // DependencyTargets defines named groups of packages that can be referenced + // by other bundles via dependsOn. Each target has a name and a list of packages. + // +optional + DependencyTargets []BundleDependencyTarget `json:"dependencyTargets,omitempty"` + + // Libraries is a list of Helm library charts used by packages + // +optional + Libraries []BundleLibrary `json:"libraries,omitempty"` + + // Artifacts is a list of Helm charts that will be built as ExternalArtifacts + // These artifacts can be referenced by ApplicationDefinitions + // +optional + Artifacts []BundleArtifact `json:"artifacts,omitempty"` + + // Packages is a list of Helm releases to be installed as part of this bundle + // +required + Packages []BundleRelease `json:"packages"` + + // DeletionPolicy defines how child resources should be handled when the bundle is deleted. + // - "Delete" (default): Child resources will be deleted when the bundle is deleted (via ownerReference). + // - "Orphan": Child resources will be orphaned (ownerReferences will be removed). + // +kubebuilder:validation:Enum=Delete;Orphan + // +kubebuilder:default=Delete + // +optional + DeletionPolicy DeletionPolicy `json:"deletionPolicy,omitempty"` + + // Labels are labels that will be applied to all resources created by this bundle + // (ArtifactGenerators and HelmReleases). These labels are merged with the default + // cozystack.io/bundle label. + // +optional + Labels map[string]string `json:"labels,omitempty"` + + // BasePath is the base path where packages are located in the source. + // For GitRepository, defaults to "packages" if not specified. + // For OCIRepository, defaults to empty string (root) if not specified. + // +optional + BasePath string `json:"basePath,omitempty"` +} + +// DeletionPolicy defines how child resources should be handled when the parent is deleted. +// +kubebuilder:validation:Enum=Delete;Orphan +type DeletionPolicy string + +const ( + // DeletionPolicyDelete means child resources will be deleted when the parent is deleted. + DeletionPolicyDelete DeletionPolicy = "Delete" + // DeletionPolicyOrphan means child resources will be orphaned (ownerReferences removed). + DeletionPolicyOrphan DeletionPolicy = "Orphan" +) + +// BundleDependencyTarget defines a named group of packages that can be referenced +// by other bundles via dependsOn +type BundleDependencyTarget struct { + // Name is the unique identifier for this dependency target + // +required + Name string `json:"name"` + + // Packages is a list of package names that belong to this target + // These packages will be added as dependencies when this target is referenced + // +required + Packages []string `json:"packages"` +} + +// BundleLibrary defines a Helm library chart +type BundleLibrary struct { + // Name is the unique identifier for this library + // +required + Name string `json:"name"` + + // Path is the path to the library chart directory + // +required + Path string `json:"path"` +} + +// BundleArtifact defines a Helm chart artifact that will be built as ExternalArtifact +type BundleArtifact struct { + // Name is the unique identifier for this artifact (used as ExternalArtifact name) + // +required + Name string `json:"name"` + + // Path is the path to the Helm chart directory + // +required + Path string `json:"path"` + + // Libraries is a list of library names that this artifact depends on + // +optional + Libraries []string `json:"libraries,omitempty"` +} + +// BundleSourceRef defines the source reference for bundle charts +type BundleSourceRef struct { + // Kind of the source reference + // +kubebuilder:validation:Enum=GitRepository;OCIRepository + // +required + Kind string `json:"kind"` + + // Name of the source reference + // +required + Name string `json:"name"` + + // Namespace of the source reference + // +required + Namespace string `json:"namespace"` +} + +// +kubebuilder:validation:XValidation:rule="(has(self.path) && !has(self.artifact)) || (!has(self.path) && has(self.artifact))",message="either path or artifact must be set, but not both" +// BundleRelease defines a single Helm release within a bundle +type BundleRelease struct { + // Name is the unique identifier for this release within the bundle + // +required + Name string `json:"name"` + + // ReleaseName is the name of the HelmRelease resource that will be created + // +required + ReleaseName string `json:"releaseName"` + + // Path is the path to the Helm chart directory + // Either Path or Artifact must be specified, but not both + // +optional + Path string `json:"path,omitempty"` + + // Artifact is the name of an artifact from the bundle's artifacts list + // The artifact must exist in the bundle's artifacts section + // Either Path or Artifact must be specified, but not both + // +optional + Artifact string `json:"artifact,omitempty"` + + // Namespace is the Kubernetes namespace where the release will be installed + // +required + Namespace string `json:"namespace"` + + // Privileged indicates whether this release requires privileged access + // +optional + Privileged bool `json:"privileged,omitempty"` + + // Disabled indicates whether this release is disabled (should not be installed) + // +optional + Disabled bool `json:"disabled,omitempty"` + + // DependsOn is a list of release names that must be installed before this release + // +optional + DependsOn []string `json:"dependsOn,omitempty"` + + // Libraries is a list of library names that this package depends on + // +optional + Libraries []string `json:"libraries,omitempty"` + + // Values contains Helm chart values as a JSON object + // +optional + Values *apiextensionsv1.JSON `json:"values,omitempty"` + + // ValuesFiles is a list of values file names to use + // +optional + ValuesFiles []string `json:"valuesFiles,omitempty"` + + // Labels are labels that will be applied to the HelmRelease created for this package + // These labels are merged with bundle-level labels and the default cozystack.io/bundle label + // +optional + Labels map[string]string `json:"labels,omitempty"` + + // NamespaceLabels are labels that will be applied to the namespace for this package + // These labels are merged with labels from other packages in the same namespace + // +optional + NamespaceLabels map[string]string `json:"namespaceLabels,omitempty"` + + // NamespaceAnnotations are annotations that will be applied to the namespace for this package + // These annotations are merged with annotations from other packages in the same namespace + // +optional + NamespaceAnnotations map[string]string `json:"namespaceAnnotations,omitempty"` +} diff --git a/api/v1alpha1/platform_types.go b/api/v1alpha1/platform_types.go new file mode 100644 index 00000000..7300bc2c --- /dev/null +++ b/api/v1alpha1/platform_types.go @@ -0,0 +1,71 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,shortName=platform + +// Platform is the Schema for the platforms API +type Platform struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PlatformSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true + +// PlatformList contains a list of Platform +type PlatformList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Platform `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Platform{}, &PlatformList{}) +} + +// PlatformSpec defines the desired state of Platform +type PlatformSpec struct { + // SourceRef is the source reference for the platform chart + // This is used to generate the ArtifactGenerator + // +required + SourceRef SourceRef `json:"sourceRef"` + + // Values contains Helm chart values as a JSON object + // These values are passed directly to HelmRelease.values + // +optional + Values *apiextensionsv1.JSON `json:"values,omitempty"` + + // Interval is the interval at which to reconcile the HelmRelease + // +kubebuilder:default="5m" + // +optional + Interval *metav1.Duration `json:"interval,omitempty"` + + // BasePath is the base path where the platform chart is located in the source. + // For GitRepository, defaults to "packages/core/platform" if not specified. + // For OCIRepository, defaults to "core/platform" if not specified. + // +optional + BasePath string `json:"basePath,omitempty"` +} + diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 42cf4c4a..744101ec 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,30 +21,32 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinition) DeepCopyInto(out *CozystackResourceDefinition) { +func (in *ApplicationDefinition) DeepCopyInto(out *ApplicationDefinition) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinition. -func (in *CozystackResourceDefinition) DeepCopy() *CozystackResourceDefinition { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinition. +func (in *ApplicationDefinition) DeepCopy() *ApplicationDefinition { if in == nil { return nil } - out := new(CozystackResourceDefinition) + out := new(ApplicationDefinition) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CozystackResourceDefinition) DeepCopyObject() runtime.Object { +func (in *ApplicationDefinition) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -52,38 +54,54 @@ func (in *CozystackResourceDefinition) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionApplication) DeepCopyInto(out *CozystackResourceDefinitionApplication) { +func (in *ApplicationDefinitionApplication) DeepCopyInto(out *ApplicationDefinitionApplication) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionApplication. -func (in *CozystackResourceDefinitionApplication) DeepCopy() *CozystackResourceDefinitionApplication { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionApplication. +func (in *ApplicationDefinitionApplication) DeepCopy() *ApplicationDefinitionApplication { if in == nil { return nil } - out := new(CozystackResourceDefinitionApplication) + out := new(ApplicationDefinitionApplication) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionChart) DeepCopyInto(out *CozystackResourceDefinitionChart) { +func (in *ApplicationDefinitionChart) DeepCopyInto(out *ApplicationDefinitionChart) { *out = *in out.SourceRef = in.SourceRef } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionChart. -func (in *CozystackResourceDefinitionChart) DeepCopy() *CozystackResourceDefinitionChart { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionChart. +func (in *ApplicationDefinitionChart) DeepCopy() *ApplicationDefinitionChart { if in == nil { return nil } - out := new(CozystackResourceDefinitionChart) + out := new(ApplicationDefinitionChart) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionDashboard) DeepCopyInto(out *CozystackResourceDefinitionDashboard) { +func (in *ApplicationDefinitionChartRef) DeepCopyInto(out *ApplicationDefinitionChartRef) { + *out = *in + out.SourceRef = in.SourceRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionChartRef. +func (in *ApplicationDefinitionChartRef) DeepCopy() *ApplicationDefinitionChartRef { + if in == nil { + return nil + } + out := new(ApplicationDefinitionChartRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinitionDashboard) DeepCopyInto(out *ApplicationDefinitionDashboard) { *out = *in if in.Tags != nil { in, out := &in.Tags, &out.Tags @@ -108,42 +126,42 @@ func (in *CozystackResourceDefinitionDashboard) DeepCopyInto(out *CozystackResou } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionDashboard. -func (in *CozystackResourceDefinitionDashboard) DeepCopy() *CozystackResourceDefinitionDashboard { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionDashboard. +func (in *ApplicationDefinitionDashboard) DeepCopy() *ApplicationDefinitionDashboard { if in == nil { return nil } - out := new(CozystackResourceDefinitionDashboard) + out := new(ApplicationDefinitionDashboard) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionList) DeepCopyInto(out *CozystackResourceDefinitionList) { +func (in *ApplicationDefinitionList) DeepCopyInto(out *ApplicationDefinitionList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]CozystackResourceDefinition, len(*in)) + *out = make([]ApplicationDefinition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionList. -func (in *CozystackResourceDefinitionList) DeepCopy() *CozystackResourceDefinitionList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionList. +func (in *ApplicationDefinitionList) DeepCopy() *ApplicationDefinitionList { if in == nil { return nil } - out := new(CozystackResourceDefinitionList) + out := new(ApplicationDefinitionList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CozystackResourceDefinitionList) DeepCopyObject() runtime.Object { +func (in *ApplicationDefinitionList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -151,9 +169,18 @@ func (in *CozystackResourceDefinitionList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionRelease) DeepCopyInto(out *CozystackResourceDefinitionRelease) { +func (in *ApplicationDefinitionRelease) DeepCopyInto(out *ApplicationDefinitionRelease) { *out = *in - out.Chart = in.Chart + if in.Chart != nil { + in, out := &in.Chart, &out.Chart + *out = new(ApplicationDefinitionChart) + **out = **in + } + if in.ChartRef != nil { + in, out := &in.ChartRef, &out.ChartRef + *out = new(ApplicationDefinitionChartRef) + **out = **in + } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -161,20 +188,25 @@ func (in *CozystackResourceDefinitionRelease) DeepCopyInto(out *CozystackResourc (*out)[key] = val } } + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = new(v1.JSON) + (*in).DeepCopyInto(*out) + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionRelease. -func (in *CozystackResourceDefinitionRelease) DeepCopy() *CozystackResourceDefinitionRelease { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionRelease. +func (in *ApplicationDefinitionRelease) DeepCopy() *ApplicationDefinitionRelease { if in == nil { return nil } - out := new(CozystackResourceDefinitionRelease) + out := new(ApplicationDefinitionRelease) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionResourceSelector) DeepCopyInto(out *CozystackResourceDefinitionResourceSelector) { +func (in *ApplicationDefinitionResourceSelector) DeepCopyInto(out *ApplicationDefinitionResourceSelector) { *out = *in in.LabelSelector.DeepCopyInto(&out.LabelSelector) if in.ResourceNames != nil { @@ -184,55 +216,55 @@ func (in *CozystackResourceDefinitionResourceSelector) DeepCopyInto(out *Cozysta } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionResourceSelector. -func (in *CozystackResourceDefinitionResourceSelector) DeepCopy() *CozystackResourceDefinitionResourceSelector { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionResourceSelector. +func (in *ApplicationDefinitionResourceSelector) DeepCopy() *ApplicationDefinitionResourceSelector { if in == nil { return nil } - out := new(CozystackResourceDefinitionResourceSelector) + out := new(ApplicationDefinitionResourceSelector) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionResources) DeepCopyInto(out *CozystackResourceDefinitionResources) { +func (in *ApplicationDefinitionResources) DeepCopyInto(out *ApplicationDefinitionResources) { *out = *in if in.Exclude != nil { in, out := &in.Exclude, &out.Exclude - *out = make([]*CozystackResourceDefinitionResourceSelector, len(*in)) + *out = make([]*ApplicationDefinitionResourceSelector, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(CozystackResourceDefinitionResourceSelector) + *out = new(ApplicationDefinitionResourceSelector) (*in).DeepCopyInto(*out) } } } if in.Include != nil { in, out := &in.Include, &out.Include - *out = make([]*CozystackResourceDefinitionResourceSelector, len(*in)) + *out = make([]*ApplicationDefinitionResourceSelector, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(CozystackResourceDefinitionResourceSelector) + *out = new(ApplicationDefinitionResourceSelector) (*in).DeepCopyInto(*out) } } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionResources. -func (in *CozystackResourceDefinitionResources) DeepCopy() *CozystackResourceDefinitionResources { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionResources. +func (in *ApplicationDefinitionResources) DeepCopy() *ApplicationDefinitionResources { if in == nil { return nil } - out := new(CozystackResourceDefinitionResources) + out := new(ApplicationDefinitionResources) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDefinitionSpec) { +func (in *ApplicationDefinitionSpec) DeepCopyInto(out *ApplicationDefinitionSpec) { *out = *in out.Application = in.Application in.Release.DeepCopyInto(&out.Release) @@ -241,17 +273,339 @@ func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDe in.Ingresses.DeepCopyInto(&out.Ingresses) if in.Dashboard != nil { in, out := &in.Dashboard, &out.Dashboard - *out = new(CozystackResourceDefinitionDashboard) + *out = new(ApplicationDefinitionDashboard) (*in).DeepCopyInto(*out) } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionSpec. -func (in *CozystackResourceDefinitionSpec) DeepCopy() *CozystackResourceDefinitionSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionSpec. +func (in *ApplicationDefinitionSpec) DeepCopy() *ApplicationDefinitionSpec { if in == nil { return nil } - out := new(CozystackResourceDefinitionSpec) + out := new(ApplicationDefinitionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Bundle) DeepCopyInto(out *Bundle) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bundle. +func (in *Bundle) DeepCopy() *Bundle { + if in == nil { + return nil + } + out := new(Bundle) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Bundle) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BundleArtifact) DeepCopyInto(out *BundleArtifact) { + *out = *in + if in.Libraries != nil { + in, out := &in.Libraries, &out.Libraries + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleArtifact. +func (in *BundleArtifact) DeepCopy() *BundleArtifact { + if in == nil { + return nil + } + out := new(BundleArtifact) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BundleDependencyTarget) DeepCopyInto(out *BundleDependencyTarget) { + *out = *in + if in.Packages != nil { + in, out := &in.Packages, &out.Packages + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleDependencyTarget. +func (in *BundleDependencyTarget) DeepCopy() *BundleDependencyTarget { + if in == nil { + return nil + } + out := new(BundleDependencyTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BundleLibrary) DeepCopyInto(out *BundleLibrary) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleLibrary. +func (in *BundleLibrary) DeepCopy() *BundleLibrary { + if in == nil { + return nil + } + out := new(BundleLibrary) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BundleList) DeepCopyInto(out *BundleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Bundle, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleList. +func (in *BundleList) DeepCopy() *BundleList { + if in == nil { + return nil + } + out := new(BundleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BundleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BundleRelease) DeepCopyInto(out *BundleRelease) { + *out = *in + if in.DependsOn != nil { + in, out := &in.DependsOn, &out.DependsOn + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Libraries != nil { + in, out := &in.Libraries, &out.Libraries + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = new(v1.JSON) + (*in).DeepCopyInto(*out) + } + if in.ValuesFiles != nil { + in, out := &in.ValuesFiles, &out.ValuesFiles + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NamespaceLabels != nil { + in, out := &in.NamespaceLabels, &out.NamespaceLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NamespaceAnnotations != nil { + in, out := &in.NamespaceAnnotations, &out.NamespaceAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleRelease. +func (in *BundleRelease) DeepCopy() *BundleRelease { + if in == nil { + return nil + } + out := new(BundleRelease) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BundleSourceRef) DeepCopyInto(out *BundleSourceRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleSourceRef. +func (in *BundleSourceRef) DeepCopy() *BundleSourceRef { + if in == nil { + return nil + } + out := new(BundleSourceRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BundleSpec) DeepCopyInto(out *BundleSpec) { + *out = *in + out.SourceRef = in.SourceRef + if in.DependsOn != nil { + in, out := &in.DependsOn, &out.DependsOn + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.DependencyTargets != nil { + in, out := &in.DependencyTargets, &out.DependencyTargets + *out = make([]BundleDependencyTarget, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Libraries != nil { + in, out := &in.Libraries, &out.Libraries + *out = make([]BundleLibrary, len(*in)) + copy(*out, *in) + } + if in.Artifacts != nil { + in, out := &in.Artifacts, &out.Artifacts + *out = make([]BundleArtifact, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Packages != nil { + in, out := &in.Packages, &out.Packages + *out = make([]BundleRelease, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleSpec. +func (in *BundleSpec) DeepCopy() *BundleSpec { + if in == nil { + return nil + } + out := new(BundleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Platform) DeepCopyInto(out *Platform) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Platform. +func (in *Platform) DeepCopy() *Platform { + if in == nil { + return nil + } + out := new(Platform) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Platform) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlatformList) DeepCopyInto(out *PlatformList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Platform, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformList. +func (in *PlatformList) DeepCopy() *PlatformList { + if in == nil { + return nil + } + out := new(PlatformList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PlatformList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlatformSpec) DeepCopyInto(out *PlatformSpec) { + *out = *in + out.SourceRef = in.SourceRef + if in.Values != nil { + in, out := &in.Values, &out.Values + *out = new(v1.JSON) + (*in).DeepCopyInto(*out) + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(metav1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformSpec. +func (in *PlatformSpec) DeepCopy() *PlatformSpec { + if in == nil { + return nil + } + out := new(PlatformSpec) in.DeepCopyInto(out) return out } diff --git a/cmd/cozystack-assets-server/main.go b/cmd/cozystack-assets-server/main.go deleted file mode 100644 index 75563712..00000000 --- a/cmd/cozystack-assets-server/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "flag" - "log" - "net/http" - "path/filepath" -) - -func main() { - addr := flag.String("address", ":8123", "Address to listen on") - dir := flag.String("dir", "/cozystack/assets", "Directory to serve files from") - flag.Parse() - - absDir, err := filepath.Abs(*dir) - if err != nil { - log.Fatalf("Error getting absolute path for %s: %v", *dir, err) - } - - fs := http.FileServer(http.Dir(absDir)) - http.Handle("/", fs) - - log.Printf("Server starting on %s, serving directory %s", *addr, absDir) - - err = http.ListenAndServe(*addr, nil) - if err != nil { - log.Fatalf("Server failed to start: %v", err) - } -} diff --git a/cmd/cozystack-controller/main.go b/cmd/cozystack-controller/main.go index fc18a778..45f46e9c 100644 --- a/cmd/cozystack-controller/main.go +++ b/cmd/cozystack-controller/main.go @@ -20,7 +20,6 @@ import ( "crypto/tls" "flag" "os" - "time" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -39,7 +38,6 @@ import ( cozystackiov1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" "github.com/cozystack/cozystack/internal/controller" "github.com/cozystack/cozystack/internal/controller/dashboard" - "github.com/cozystack/cozystack/internal/telemetry" helmv2 "github.com/fluxcd/helm-controller/api/v2" // +kubebuilder:scaffold:imports @@ -65,10 +63,6 @@ func main() { var probeAddr string var secureMetrics bool var enableHTTP2 bool - var disableTelemetry bool - var telemetryEndpoint string - var telemetryInterval string - var cozystackVersion string var reconcileDeployment bool var tlsOpts []func(*tls.Config) flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ @@ -81,14 +75,6 @@ func main() { "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers") - flag.BoolVar(&disableTelemetry, "disable-telemetry", false, - "Disable telemetry collection") - flag.StringVar(&telemetryEndpoint, "telemetry-endpoint", "https://telemetry.cozystack.io", - "Endpoint for sending telemetry data") - flag.StringVar(&telemetryInterval, "telemetry-interval", "15m", - "Interval between telemetry data collection (e.g. 15m, 1h)") - flag.StringVar(&cozystackVersion, "cozystack-version", "unknown", - "Version of Cozystack") flag.BoolVar(&reconcileDeployment, "reconcile-deployment", false, "If set, the Cozystack API server is assumed to run as a Deployment, else as a DaemonSet.") opts := zap.Options{ @@ -97,21 +83,6 @@ func main() { opts.BindFlags(flag.CommandLine) flag.Parse() - // Parse telemetry interval - interval, err := time.ParseDuration(telemetryInterval) - if err != nil { - setupLog.Error(err, "invalid telemetry interval") - os.Exit(1) - } - - // Configure telemetry - telemetryConfig := telemetry.Config{ - Disabled: disableTelemetry, - Endpoint: telemetryEndpoint, - Interval: interval, - CozystackVersion: cozystackVersion, - } - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) // if the enable-http2 flag is false (the default), http/2 should be disabled @@ -200,32 +171,20 @@ func main() { os.Exit(1) } - if err = (&controller.TenantHelmReconciler{ + if err = (&controller.NamespaceHelmReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "TenantHelmReconciler") + setupLog.Error(err, "unable to create controller", "controller", "NamespaceHelmReconciler") os.Exit(1) } - if err = (&controller.CozystackConfigReconciler{ + if err = (&controller.ApplicationDefinitionReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), + CozystackAPIKind: "Deployment", }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "CozystackConfigReconciler") - os.Exit(1) - } - - cozyAPIKind := "DaemonSet" - if reconcileDeployment { - cozyAPIKind = "Deployment" - } - if err = (&controller.CozystackResourceDefinitionReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - CozystackAPIKind: cozyAPIKind, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionReconciler") + setupLog.Error(err, "unable to create controller", "controller", "ApplicationDefinitionReconciler") os.Exit(1) } @@ -249,19 +208,6 @@ func main() { os.Exit(1) } - // Initialize telemetry collector - collector, err := telemetry.NewCollector(mgr.GetClient(), &telemetryConfig, mgr.GetConfig()) - if err != nil { - setupLog.V(1).Error(err, "unable to create telemetry collector, telemetry will be disabled") - } - - if collector != nil { - if err := mgr.Add(collector); err != nil { - setupLog.Error(err, "unable to set up telemetry collector") - setupLog.V(1).Error(err, "unable to set up telemetry collector, continuing without telemetry") - } - } - setupLog.Info("starting manager") ctx := ctrl.SetupSignalHandler() dashboardManager.InitializeStaticResources(ctx) diff --git a/cmd/cozystack-operator/main.go b/cmd/cozystack-operator/main.go new file mode 100644 index 00000000..3cdb2371 --- /dev/null +++ b/cmd/cozystack-operator/main.go @@ -0,0 +1,312 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "os" + "strings" + "time" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + helmv2 "github.com/fluxcd/helm-controller/api/v2" + sourcev1 "github.com/fluxcd/source-controller/api/v1" + sourcewatcherv1beta1 "github.com/fluxcd/source-watcher/api/v2/v1beta1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/cozystack/cozystack/internal/fluxinstall" + "github.com/cozystack/cozystack/internal/operator" + "github.com/cozystack/cozystack/internal/telemetry" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +// stringSliceFlag is a custom flag type that allows multiple values +type stringSliceFlag []string + +func (f *stringSliceFlag) String() string { + return strings.Join(*f, ",") +} + +func (f *stringSliceFlag) Set(value string) error { + *f = append(*f, value) + return nil +} + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) + utilruntime.Must(cozyv1alpha1.AddToScheme(scheme)) + utilruntime.Must(helmv2.AddToScheme(scheme)) + utilruntime.Must(sourcev1.AddToScheme(scheme)) + utilruntime.Must(sourcewatcherv1beta1.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme +} + +func main() { + var metricsAddr string + var enableLeaderElection bool + var probeAddr string + var secureMetrics bool + var enableHTTP2 bool + var installFlux bool + var disableTelemetry bool + var telemetryEndpoint string + var telemetryInterval string + var cozystackVersion string + var installFluxResources stringSliceFlag + + flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.BoolVar(&secureMetrics, "metrics-secure", false, + "If set the metrics endpoint is served securely") + flag.BoolVar(&enableHTTP2, "enable-http2", false, + "If set, HTTP/2 will be enabled for the metrics and webhook servers") + flag.BoolVar(&installFlux, "install-flux", false, "Install Flux components before starting reconcile loop") + flag.Var(&installFluxResources, "install-flux-resource", "Install Flux resource (JSON format). Can be specified multiple times. Applied after Flux installation.") + flag.BoolVar(&disableTelemetry, "disable-telemetry", false, + "Disable telemetry collection") + flag.StringVar(&telemetryEndpoint, "telemetry-endpoint", "https://telemetry.cozystack.io", + "Endpoint for sending telemetry data") + flag.StringVar(&telemetryInterval, "telemetry-interval", "15m", + "Interval between telemetry data collection (e.g. 15m, 1h)") + flag.StringVar(&cozystackVersion, "cozystack-version", "unknown", + "Version of Cozystack") + + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + // Parse telemetry interval + interval, err := time.ParseDuration(telemetryInterval) + if err != nil { + setupLog.Error(err, "invalid telemetry interval") + os.Exit(1) + } + + // Configure telemetry + telemetryConfig := telemetry.Config{ + Disabled: disableTelemetry, + Endpoint: telemetryEndpoint, + Interval: interval, + CozystackVersion: cozystackVersion, + } + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + config := ctrl.GetConfigOrDie() + + // Start the controller manager + setupLog.Info("Starting controller manager") + mgr, err := ctrl.NewManager(config, ctrl.Options{ + Scheme: scheme, + Metrics: metricsserver.Options{ + BindAddress: metricsAddr, + SecureServing: secureMetrics, + }, + WebhookServer: webhook.NewServer(webhook.Options{ + Port: 9443, + }), + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "platform-operator.cozystack.io", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, setting this significantly speeds up voluntary + // leader transitions as the new leader don't have to wait LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + // Install Flux before starting reconcile loop + if installFlux { + setupLog.Info("Installing Flux components before starting reconcile loop") + installCtx, installCancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer installCancel() + + // The namespace will be automatically extracted from the embedded manifests + if err := fluxinstall.Install(installCtx, mgr.GetClient(), fluxinstall.WriteEmbeddedManifests); err != nil { + setupLog.Error(err, "failed to install Flux, continuing anyway") + // Don't exit - allow operator to start even if Flux install fails + // This allows the operator to work in environments where Flux is already installed + } else { + setupLog.Info("Flux installation completed successfully") + } + } + + // Install Flux resources after Flux installation + if len(installFluxResources) > 0 { + setupLog.Info("Installing Flux resources", "count", len(installFluxResources)) + installCtx, installCancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer installCancel() + + if err := installFluxResourcesFunc(installCtx, mgr.GetClient(), installFluxResources); err != nil { + setupLog.Error(err, "failed to install Flux resources, continuing anyway") + // Don't exit - allow operator to start even if resource installation fails + } else { + setupLog.Info("Flux resources installation completed successfully") + } + } + + bundleReconciler := &operator.BundleReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + } + if err = bundleReconciler.SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Bundle") + os.Exit(1) + } + + platformReconciler := &operator.PlatformReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + } + if err = platformReconciler.SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Platform") + os.Exit(1) + } + + // +kubebuilder:scaffold:builder + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + // Initialize telemetry collector + collector, err := telemetry.NewCollector(mgr.GetClient(), &telemetryConfig, mgr.GetConfig()) + if err != nil { + setupLog.V(1).Error(err, "unable to create telemetry collector, telemetry will be disabled") + } + + if collector != nil { + if err := mgr.Add(collector); err != nil { + setupLog.Error(err, "unable to set up telemetry collector") + setupLog.V(1).Error(err, "unable to set up telemetry collector, continuing without telemetry") + } + } + + setupLog.Info("Starting controller manager") + mgrCtx := ctrl.SetupSignalHandler() + if err := mgr.Start(mgrCtx); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} + +// installFluxResourcesFunc installs Flux resources from JSON strings +func installFluxResourcesFunc(ctx context.Context, k8sClient client.Client, resources []string) error { + logger := log.FromContext(ctx) + + for i, resourceJSON := range resources { + logger.Info("Installing Flux resource", "index", i+1, "total", len(resources)) + + // Parse JSON into unstructured object + var obj unstructured.Unstructured + if err := json.Unmarshal([]byte(resourceJSON), &obj.Object); err != nil { + return fmt.Errorf("failed to parse resource JSON at index %d: %w", i, err) + } + + // Validate that it has required fields + if obj.GetAPIVersion() == "" { + return fmt.Errorf("resource at index %d missing apiVersion", i) + } + if obj.GetKind() == "" { + return fmt.Errorf("resource at index %d missing kind", i) + } + if obj.GetName() == "" { + return fmt.Errorf("resource at index %d missing metadata.name", i) + } + + // Apply the resource (create or update) + logger.Info("Applying Flux resource", + "apiVersion", obj.GetAPIVersion(), + "kind", obj.GetKind(), + "name", obj.GetName(), + "namespace", obj.GetNamespace(), + ) + + // Use server-side apply or create/update + existing := &unstructured.Unstructured{} + existing.SetGroupVersionKind(obj.GroupVersionKind()) + key := client.ObjectKey{ + Name: obj.GetName(), + Namespace: obj.GetNamespace(), + } + + err := k8sClient.Get(ctx, key, existing) + if err != nil { + if client.IgnoreNotFound(err) == nil { + // Resource doesn't exist, create it + if err := k8sClient.Create(ctx, &obj); err != nil { + return fmt.Errorf("failed to create resource %s/%s: %w", obj.GetKind(), obj.GetName(), err) + } + logger.Info("Created Flux resource", "kind", obj.GetKind(), "name", obj.GetName()) + } else { + return fmt.Errorf("failed to check if resource exists: %w", err) + } + } else { + // Resource exists, update it + obj.SetResourceVersion(existing.GetResourceVersion()) + if err := k8sClient.Update(ctx, &obj); err != nil { + return fmt.Errorf("failed to update resource %s/%s: %w", obj.GetKind(), obj.GetName(), err) + } + logger.Info("Updated Flux resource", "kind", obj.GetKind(), "name", obj.GetName()) + } + } + + return nil +} diff --git a/examples/platform-example.yaml b/examples/platform-example.yaml new file mode 100644 index 00000000..6e1a13ee --- /dev/null +++ b/examples/platform-example.yaml @@ -0,0 +1,31 @@ +apiVersion: cozystack.io/v1alpha1 +kind: Platform +metadata: + name: cozystack-platform + # Cluster-scoped resource, no namespace needed +spec: + # SourceRef is required - reference to the OCIRepository or GitRepository + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + + # Optional: Interval for HelmRelease reconciliation (default: 5m) + interval: 5m + + # Optional: BasePath is the base path where the platform chart is located in the source. + # For GitRepository, defaults to "packages/core/platform" if not specified. + # For OCIRepository, defaults to "core/platform" if not specified. + # basePath: core/platform + + # Optional: Values to pass to HelmRelease + # These values will be merged with sourceRef (which is automatically added) + values: + # Any custom values can be added here + # sourceRef will be automatically added by the controller + # Example custom values: + # customKey: customValue + # nested: + # config: + # enabled: true + diff --git a/go.mod b/go.mod index 041aa96a..1bad2023 100644 --- a/go.mod +++ b/go.mod @@ -2,33 +2,39 @@ module github.com/cozystack/cozystack -go 1.23.0 +go 1.25.0 require ( - github.com/fluxcd/helm-controller/api v1.1.0 + github.com/fluxcd/helm-controller/api v1.4.3 + github.com/fluxcd/source-controller/api v1.6.2 + github.com/fluxcd/source-watcher/api/v2 v2.0.2 + github.com/go-logr/logr v1.4.3 + github.com/go-logr/zapr v1.3.0 github.com/google/gofuzz v1.2.0 - github.com/onsi/ginkgo/v2 v2.19.0 - github.com/onsi/gomega v1.33.1 - github.com/spf13/cobra v1.8.1 - github.com/stretchr/testify v1.9.0 + github.com/onsi/ginkgo/v2 v2.23.3 + github.com/onsi/gomega v1.37.0 + github.com/prometheus/client_golang v1.22.0 + github.com/spf13/cobra v1.9.1 + github.com/stretchr/testify v1.11.1 + go.uber.org/zap v1.27.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.31.2 - k8s.io/apiextensions-apiserver v0.31.2 - k8s.io/apimachinery v0.31.2 - k8s.io/apiserver v0.31.2 - k8s.io/client-go v0.31.2 - k8s.io/component-base v0.31.2 + k8s.io/api v0.34.1 + k8s.io/apiextensions-apiserver v0.34.1 + k8s.io/apimachinery v0.34.1 + k8s.io/apiserver v0.34.1 + k8s.io/client-go v0.34.1 + k8s.io/component-base v0.34.1 k8s.io/klog/v2 v2.130.1 - k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2 - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 - sigs.k8s.io/controller-runtime v0.19.0 - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 + k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b + k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d + sigs.k8s.io/controller-runtime v0.22.2 + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 ) require ( + cel.dev/expr v0.24.0 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -36,86 +42,88 @@ require ( github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fluxcd/pkg/apis/kustomize v1.6.1 // indirect - github.com/fluxcd/pkg/apis/meta v1.6.1 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/fluxcd/pkg/apis/acl v0.9.0 // indirect + github.com/fluxcd/pkg/apis/kustomize v1.13.0 // indirect + github.com/fluxcd/pkg/apis/meta v1.22.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/cel-go v0.21.0 // indirect - github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/cel-go v0.26.0 // indirect + github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/moby/spdystream v0.4.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/moby/spdystream v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.7 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect - go.etcd.io/etcd/api/v3 v3.5.16 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect - go.etcd.io/etcd/client/v3 v3.5.16 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect - go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.etcd.io/etcd/api/v3 v3.6.4 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect + go.etcd.io/etcd/client/v3 v3.6.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/sdk v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.31.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/net v0.45.0 // indirect + golang.org/x/oauth2 v0.29.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.11.0 // indirect + golang.org/x/tools v0.37.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/grpc v1.72.1 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/kms v0.31.2 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + k8s.io/kms v0.34.1 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index a1090f31..d69a78f6 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -18,45 +18,48 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= -github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fluxcd/helm-controller/api v1.1.0 h1:NS5Wm3U6Kv4w7Cw2sDOV++vf2ecGfFV00x1+2Y3QcOY= -github.com/fluxcd/helm-controller/api v1.1.0/go.mod h1:BgHMgMY6CWynzl4KIbHpd6Wpn3FN9BqgkwmvoKCp6iE= -github.com/fluxcd/pkg/apis/kustomize v1.6.1 h1:22FJc69Mq4i8aCxnKPlddHhSMyI4UPkQkqiAdWFcqe0= -github.com/fluxcd/pkg/apis/kustomize v1.6.1/go.mod h1:5dvQ4IZwz0hMGmuj8tTWGtarsuxW0rWsxJOwC6i+0V8= -github.com/fluxcd/pkg/apis/meta v1.6.1 h1:maLhcRJ3P/70ArLCY/LF/YovkxXbX+6sTWZwZQBeNq0= -github.com/fluxcd/pkg/apis/meta v1.6.1/go.mod h1:YndB/gxgGZmKfqpAfFxyCDNFJFP0ikpeJzs66jwq280= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fluxcd/helm-controller/api v1.4.3 h1:CdZwjL1liXmYCWyk2jscmFEB59tICIlnWB9PfDDW5q4= +github.com/fluxcd/helm-controller/api v1.4.3/go.mod h1:0XrBhKEaqvxyDj/FziG1Q8Fmx2UATdaqLgYqmZh6wW4= +github.com/fluxcd/pkg/apis/acl v0.9.0 h1:wBpgsKT+jcyZEcM//OmZr9RiF8klL3ebrDp2u2ThsnA= +github.com/fluxcd/pkg/apis/acl v0.9.0/go.mod h1:TttNS+gocsGLwnvmgVi3/Yscwqrjc17+vhgYfqkfrV4= +github.com/fluxcd/pkg/apis/kustomize v1.13.0 h1:GGf0UBVRIku+gebY944icVeEIhyg1P/KE3IrhOyJJnE= +github.com/fluxcd/pkg/apis/kustomize v1.13.0/go.mod h1:TLKVqbtnzkhDuhWnAsN35977HvRfIjs+lgMuNro/LEc= +github.com/fluxcd/pkg/apis/meta v1.22.0 h1:EHWQH5ZWml7i8eZ/AMjm1jxid3j/PQ31p+hIwCt6crM= +github.com/fluxcd/pkg/apis/meta v1.22.0/go.mod h1:Kc1+bWe5p0doROzuV9XiTfV/oL3ddsemYXt8ZYWdVVg= +github.com/fluxcd/source-controller/api v1.6.2 h1:UmodAeqLIeF29HdTqf2GiacZyO+hJydJlepDaYsMvhc= +github.com/fluxcd/source-controller/api v1.6.2/go.mod h1:ZJcAi0nemsnBxjVgmJl0WQzNvB0rMETxQMTdoFosmMw= +github.com/fluxcd/source-watcher/api/v2 v2.0.2 h1:fWSxsDqYN7My2AEpQwbP7O6Qjix8nGBX+UE/qWHtZfM= +github.com/fluxcd/source-watcher/api/v2 v2.0.2/go.mod h1:Hs6ueayPt23jlkIr/d1pGPZ+OHiibQwWjxvU6xqljzg= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -64,162 +67,169 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI= -github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= +github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= +github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0= +github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= +github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= -go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0= -go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28= -go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q= -go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E= -go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8= -go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg= -go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE= -go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50= -go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M= -go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0= -go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA= -go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw= -go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok= -go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I= +go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= +go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= +go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= +go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= +go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= +go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= +go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= +go.etcd.io/etcd/pkg/v3 v3.6.4 h1:fy8bmXIec1Q35/jRZ0KOes8vuFxbvdN0aAFqmEfJZWA= +go.etcd.io/etcd/pkg/v3 v3.6.4/go.mod h1:kKcYWP8gHuBRcteyv6MXWSN0+bVMnfgqiHueIZnKMtE= +go.etcd.io/etcd/server/v3 v3.6.4 h1:LsCA7CzjVt+8WGrdsnh6RhC0XqCsLkBly3ve5rTxMAU= +go.etcd.io/etcd/server/v3 v3.6.4/go.mod h1:aYCL/h43yiONOv0QIR82kH/2xZ7m+IWYjzRmyQfnCAg= +go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ= +go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= +go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -228,50 +238,48 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= +golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= -google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -281,39 +289,44 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= -k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= -k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= -k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM= -k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= -k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= -k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE= -k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= -k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= -k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= -k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= +k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= +k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= +k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= +k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= +k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= +k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= +k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= +k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= +k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= +k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kms v0.31.2 h1:pyx7l2qVOkClzFMIWMVF/FxsSkgd+OIGH7DecpbscJI= -k8s.io/kms v0.31.2/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94= -k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2 h1:GKE9U8BH16uynoxQii0auTjmmmuZ3O0LFMN6S0lPPhI= -k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= -sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +k8s.io/kms v0.34.1 h1:iCFOvewDPzWM9fMTfyIPO+4MeuZ0tcZbugxLNSHFG4w= +k8s.io/kms v0.34.1/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= +k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.22.2 h1:cK2l8BGWsSWkXz09tcS4rJh95iOLney5eawcK5A33r4= +sigs.k8s.io/controller-runtime v0.22.2/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/scripts/common-envs.mk b/hack/common-envs.mk similarity index 100% rename from scripts/common-envs.mk rename to hack/common-envs.mk diff --git a/hack/cozyreport.sh b/hack/cozyreport.sh index 3174ed50..3fde840d 100755 --- a/hack/cozyreport.sh +++ b/hack/cozyreport.sh @@ -12,13 +12,19 @@ command -V tar >/dev/null || exit $? echo "Collecting Cozystack information..." mkdir -p $REPORT_DIR/cozystack -kubectl get deploy -n cozy-system cozystack -o jsonpath='{.spec.template.spec.containers[0].image}' > $REPORT_DIR/cozystack/image.txt 2>&1 -kubectl get cm -n cozy-system --no-headers | awk '$1 ~ /^cozystack/' | - while read NAME _; do - DIR=$REPORT_DIR/cozystack/configs - mkdir -p $DIR - kubectl get cm -n cozy-system $NAME -o yaml > $DIR/$NAME.yaml 2>&1 - done +kubectl get deploy -n cozy-system cozystack-operator cozystack-controller -o yaml > $REPORT_DIR/cozystack/deployments.yaml 2>&1 + +echo "Collecting platforms..." +kubectl get platforms.cozystack.io -A > $REPORT_DIR/cozystack/platforms.txt 2>&1 +kubectl get platforms.cozystack.io -A -o yaml > $REPORT_DIR/cozystack/platforms.yaml 2>&1 + +echo "Collecting bundles..." +kubectl get bundles.cozystack.io -A > $REPORT_DIR/cozystack/bundles.txt 2>&1 +kubectl get bundles.cozystack.io -A -o yaml > $REPORT_DIR/cozystack/bundles.yaml 2>&1 + +echo "Collecting applicationdefinitions..." +kubectl get applicationdefinitions.cozystack.io -A > $REPORT_DIR/cozystack/applicationdefinitions.txt 2>&1 +kubectl get applicationdefinitions.cozystack.io -A -o yaml > $REPORT_DIR/cozystack/applicationdefinitions.yaml 2>&1 # -- kubernetes module @@ -56,6 +62,36 @@ kubectl get hr -A --no-headers | awk '$4 != "True"' | \ kubectl describe hr -n $NAMESPACE $NAME > $DIR/describe.txt 2>&1 done +echo "Collecting artifactgenerators..." +kubectl get artifactgenerators.source.extensions.fluxcd.io -A > $REPORT_DIR/kubernetes/artifactgenerators.txt 2>&1 +kubectl get artifactgenerators.source.extensions.fluxcd.io -A --no-headers | awk '$4 != "True"' | \ + while read NAMESPACE NAME _; do + DIR=$REPORT_DIR/kubernetes/artifactgenerators/$NAMESPACE/$NAME + mkdir -p $DIR + kubectl get artifactgenerators.source.extensions.fluxcd.io -n $NAMESPACE $NAME -o yaml > $DIR/artifactgenerator.yaml 2>&1 + kubectl describe artifactgenerators.source.extensions.fluxcd.io -n $NAMESPACE $NAME > $DIR/describe.txt 2>&1 + done + +echo "Collecting ocirepositories..." +kubectl get ocirepositories.source.toolkit.fluxcd.io -A > $REPORT_DIR/kubernetes/ocirepositories.txt 2>&1 +kubectl get ocirepositories.source.toolkit.fluxcd.io -A --no-headers | awk '$4 != "True"' | \ + while read NAMESPACE NAME _; do + DIR=$REPORT_DIR/kubernetes/ocirepositories/$NAMESPACE/$NAME + mkdir -p $DIR + kubectl get ocirepositories.source.toolkit.fluxcd.io -n $NAMESPACE $NAME -o yaml > $DIR/ocirepository.yaml 2>&1 + kubectl describe ocirepositories.source.toolkit.fluxcd.io -n $NAMESPACE $NAME > $DIR/describe.txt 2>&1 + done + +echo "Collecting gitrepositories..." +kubectl get gitrepositories.source.toolkit.fluxcd.io -A > $REPORT_DIR/kubernetes/gitrepositories.txt 2>&1 +kubectl get gitrepositories.source.toolkit.fluxcd.io -A --no-headers | awk '$4 != "True"' | \ + while read NAMESPACE NAME _; do + DIR=$REPORT_DIR/kubernetes/gitrepositories/$NAMESPACE/$NAME + mkdir -p $DIR + kubectl get gitrepositories.source.toolkit.fluxcd.io -n $NAMESPACE $NAME -o yaml > $DIR/gitrepository.yaml 2>&1 + kubectl describe gitrepositories.source.toolkit.fluxcd.io -n $NAMESPACE $NAME > $DIR/describe.txt 2>&1 + done + echo "Collecting pods..." kubectl get pod -A -o wide > $REPORT_DIR/kubernetes/pods.txt 2>&1 kubectl get pod -A --no-headers | awk '$4 !~ /Running|Succeeded|Completed/' | diff --git a/hack/e2e-install-cozystack.bats b/hack/e2e-install-cozystack.bats index da132132..73f4f0f1 100644 --- a/hack/e2e-install-cozystack.bats +++ b/hack/e2e-install-cozystack.bats @@ -8,23 +8,51 @@ } @test "Install Cozystack" { - # Create namespace & configmap required by installer - kubectl create namespace cozy-system --dry-run=client -o yaml | kubectl apply -f - - kubectl create configmap cozystack -n cozy-system \ - --from-literal=bundle-name=paas-full \ - --from-literal=ipv4-pod-cidr=10.244.0.0/16 \ - --from-literal=ipv4-pod-gateway=10.244.0.1 \ - --from-literal=ipv4-svc-cidr=10.96.0.0/16 \ - --from-literal=ipv4-join-cidr=100.64.0.0/16 \ - --from-literal=root-host=example.org \ - --from-literal=api-server-endpoint=https://192.168.123.10:6443 \ - --dry-run=client -o yaml | kubectl apply -f - - # Apply installer manifests from file kubectl apply -f _out/assets/cozystack-installer.yaml # Wait for the installer deployment to become available - kubectl wait deployment/cozystack -n cozy-system --timeout=1m --for=condition=Available + kubectl wait deployment/cozystack-operator -n cozy-system --timeout=1m --for=condition=Available + + # Wait for cozy-fluxcd namespace to be created + timeout 30 sh -ec 'until kubectl get namespace cozy-fluxcd >/dev/null 2>&1; do sleep 1; done' + + # Wait for Flux deployment + timeout 30 sh -ec 'until kubectl get deployment/flux -n cozy-fluxcd >/dev/null 2>&1; do sleep 1; done' + kubectl wait deployment/flux -n cozy-fluxcd --timeout=1m --for=condition=Available + + # Create Platform resource instead of configmap + kubectl apply -f - <<'EOF' +apiVersion: cozystack.io/v1alpha1 +kind: Platform +metadata: + name: cozystack-platform +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + values: + bundles: + system: + type: "full" + networking: + podCIDR: "10.244.0.0/16" + podGateway: "10.244.0.1" + serviceCIDR: "10.96.0.0/16" + joinCIDR: "100.64.0.0/16" + publishing: + host: "example.org" + apiServerEndpoint: "https://192.168.123.10:6443" +EOF + + # Wait for ArtifactGenerator for cozystack-packages + timeout 60 sh -ec 'until kubectl get artifactgenerators.source.extensions.fluxcd.io cozystack-packages -n cozy-system >/dev/null 2>&1; do sleep 1; done' + kubectl wait artifactgenerators.source.extensions.fluxcd.io/cozystack-packages -n cozy-system --for=condition=ready --timeout=5m + + # Wait for bundle ArtifactGenerators + timeout 60 sh -ec 'until kubectl get artifactgenerators.source.extensions.fluxcd.io cozystack-system cozystack-iaas cozystack-paas cozystack-naas -n cozy-system >/dev/null 2>&1; do sleep 1; done' + kubectl wait artifactgenerators.source.extensions.fluxcd.io -n cozy-system --for=condition=ready --timeout=5m cozystack-system cozystack-iaas cozystack-paas cozystack-naas # Wait until HelmReleases appear & reconcile them timeout 60 sh -ec 'until kubectl get hr -A -l cozystack.io/system-app=true | grep -q cozys; do sleep 1; done' @@ -140,9 +168,8 @@ EOF kubectl wait hr/seaweedfs-system -n tenant-root --timeout=2m --for=condition=ready fi - # Expose Cozystack services through ingress - kubectl patch configmap/cozystack -n cozy-system --type merge -p '{"data":{"expose-services":"api,dashboard,cdi-uploadproxy,vm-exportproxy,keycloak"}}' + kubectl patch platform/cozystack-platform --type merge -p '{"spec":{"values":{"publishing":{"exposedServices":["api","dashboard","cdi-uploadproxy","vm-exportproxy","keycloak"]}}}}' # NGINX ingress controller timeout 60 sh -ec 'until kubectl get deploy root-ingress-controller -n tenant-root >/dev/null 2>&1; do sleep 1; done' @@ -169,7 +196,7 @@ EOF } @test "Keycloak OIDC stack is healthy" { - kubectl patch configmap/cozystack -n cozy-system --type merge -p '{"data":{"oidc-enabled":"true"}}' + kubectl patch platform/cozystack-platform --type merge -p '{"spec":{"values":{"authentication":{"oidc":{"enabled":true}}}}}' timeout 120 sh -ec 'until kubectl get hr -n cozy-keycloak keycloak keycloak-configure keycloak-operator >/dev/null 2>&1; do sleep 1; done' kubectl wait hr/keycloak hr/keycloak-configure hr/keycloak-operator -n cozy-keycloak --timeout=10m --for=condition=ready diff --git a/scripts/package.mk b/hack/package.mk similarity index 100% rename from scripts/package.mk rename to hack/package.mk diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index e181fc76..664a8588 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -54,5 +54,9 @@ kube::codegen::gen_openapi \ $CONTROLLER_GEN object:headerFile="hack/boilerplate.go.txt" paths="./api/..." $CONTROLLER_GEN rbac:roleName=manager-role crd paths="./api/..." output:crd:artifacts:config=packages/system/cozystack-controller/crds -mv packages/system/cozystack-controller/crds/cozystack.io_cozystackresourcedefinitions.yaml \ - packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml +mv packages/system/cozystack-controller/crds/cozystack.io_applicationdefinitions.yaml \ + packages/core/installer/crds/cozystack.io_applicationdefinitions.yaml +mv packages/system/cozystack-controller/crds/cozystack.io_bundles.yaml \ + packages/core/installer/crds/cozystack.io_bundles.yaml +mv packages/system/cozystack-controller/crds/cozystack.io_platforms.yaml \ + packages/core/installer/crds/cozystack.io_platforms.yaml diff --git a/hack/update-crd.sh b/hack/update-crd.sh index 456bf842..620aeac4 100755 --- a/hack/update-crd.sh +++ b/hack/update-crd.sh @@ -8,7 +8,7 @@ need yq; need jq; need base64 CHART_YAML="${CHART_YAML:-Chart.yaml}" VALUES_YAML="${VALUES_YAML:-values.yaml}" SCHEMA_JSON="${SCHEMA_JSON:-values.schema.json}" -CRD_DIR="../../system/cozystack-resource-definitions/cozyrds" +CRD_DIR="../../core/platform/bundles/*/applicationdefinitions" [[ -f "$CHART_YAML" ]] || { echo "No $CHART_YAML found"; exit 1; } [[ -f "$SCHEMA_JSON" ]] || { echo "No $SCHEMA_JSON found"; exit 1; } @@ -54,37 +54,71 @@ fi # Base64 (portable: no -w / -b options) ICON_B64="$(base64 < "$ICON_PATH" | tr -d '\n' | tr -d '\r')" -# Decide which HelmRepository name to use based on path -# .../apps/... -> cozystack-apps -# .../extra/... -> cozystack-extra -# default: cozystack-apps -SOURCE_NAME="cozystack-apps" -case "$PWD" in - *"/apps/"*) SOURCE_NAME="cozystack-apps" ;; - *"/extra/"*) SOURCE_NAME="cozystack-extra" ;; -esac +# Find path to output CRD YAML +OUT="$(find $CRD_DIR -type f -name "${NAME}.yaml" | head -n 1)" +if [[ -z "$OUT" ]]; then + echo "Error: ApplicationDefinition file for '${NAME}' not found in ${CRD_DIR}" + echo "Please create the file first in one of the following directories:" + + # Auto-detect existing directories + BASE_DIR="../../core/platform/bundles" + if [[ -d "$BASE_DIR" ]]; then + for bundle_dir in "$BASE_DIR"/*/applicationdefinitions; do + if [[ -d "$bundle_dir" ]]; then + bundle_name="$(basename "$(dirname "$bundle_dir")")" + echo " touch ${bundle_dir}/${NAME}.yaml # ${bundle_name}" + fi + done + else + # Fallback if base directory doesn't exist + echo " touch ../../core/platform/bundles/iaas/applicationdefinitions/${NAME}.yaml" + echo " touch ../../core/platform/bundles/paas/applicationdefinitions/${NAME}.yaml" + echo " touch ../../core/platform/bundles/naas/applicationdefinitions/${NAME}.yaml" + echo " touch ../../core/platform/bundles/system/applicationdefinitions/${NAME}.yaml" + fi + exit 1 +fi -# If file doesn't exist, create a minimal skeleton -OUT="${OUT:-$CRD_DIR/$NAME.yaml}" -if [[ ! -f "$OUT" ]]; then +if [[ ! -s "$OUT" ]]; then cat >"$OUT" < "${OUT}.tmp" && mv "${OUT}.tmp" "$OUT" +fi + # Update only necessary fields in-place # - openAPISchema is loaded from file as a multi-line string (block scalar) # - labels ensure cozystack.io/ui: "true" @@ -121,19 +161,26 @@ export KEYS_ORDER="$( # - sourceRef derived from directory (apps|extra) yq -i ' .apiVersion = (.apiVersion // "cozystack.io/v1alpha1") | - .kind = (.kind // "CozystackResourceDefinition") | + .kind = (.kind // "ApplicationDefinition") | .metadata.name = strenv(RES_NAME) | .spec.application.openAPISchema = strenv(SCHEMA_JSON_MIN) | (.spec.application.openAPISchema style="literal") | .spec.release.prefix = (strenv(PREFIX)) | .spec.release.labels."cozystack.io/ui" = "true" | - .spec.release.chart.name = strenv(RES_NAME) | - .spec.release.chart.sourceRef.kind = "HelmRepository" | - .spec.release.chart.sourceRef.name = strenv(SOURCE_NAME) | - .spec.release.chart.sourceRef.namespace = "cozy-public" | + del(.spec.release.chart) | + .spec.release.chartRef.sourceRef.kind = "ExternalArtifact" | + .spec.release.chartRef.sourceRef.name = strenv(ARTIFACT_NAME) | + .spec.release.chartRef.sourceRef.namespace = "cozy-system" | .spec.dashboard.description = strenv(DESCRIPTION) | .spec.dashboard.icon = strenv(ICON_B64) | .spec.dashboard.keysOrder = env(KEYS_ORDER) ' "$OUT" +# Add back the Helm template line after _cozystack +if [[ -f "$OUT" && -n "$OUT" ]]; then + HELM_TEMPLATE=' {{- include "cozystack.build-values" . | nindent 8 }}' + # Use awk for more reliable insertion + awk -v template="$HELM_TEMPLATE" '/_cozystack:/ {print; print template; next} {print}' "$OUT" > "${OUT}.tmp" && mv "${OUT}.tmp" "$OUT" +fi + echo "Updated $OUT" diff --git a/internal/controller/applicationdefinition_controller.go b/internal/controller/applicationdefinition_controller.go new file mode 100644 index 00000000..8eb58901 --- /dev/null +++ b/internal/controller/applicationdefinition_controller.go @@ -0,0 +1,502 @@ +package controller + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "slices" + "sync" + "time" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + helmv2 "github.com/fluxcd/helm-controller/api/v2" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/cozystack/cozystack/pkg/cozylib" +) + +// +kubebuilder:rbac:groups=cozystack.io,resources=applicationdefinitions,verbs=get;list;watch +// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch;update;patch +// +kubebuilder:rbac:groups=core,resources=namespaces,verbs=get;list;watch +type ApplicationDefinitionReconciler struct { + client.Client + Scheme *runtime.Scheme + + Debounce time.Duration + + mu sync.Mutex + lastEvent time.Time + lastHandled time.Time + + CozystackAPIKind string +} + +func (r *ApplicationDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconciling ApplicationDefinitions", "request", req.NamespacedName) + + // Get all ApplicationDefinitions + crdList := &cozyv1alpha1.ApplicationDefinitionList{} + if err := r.List(ctx, crdList); err != nil { + logger.Error(err, "failed to list ApplicationDefinitions") + return ctrl.Result{}, err + } + + logger.Info("Found ApplicationDefinitions", "count", len(crdList.Items)) + + // Update HelmReleases for each CRD + for i := range crdList.Items { + crd := &crdList.Items[i] + logger.V(4).Info("Processing CRD", "crd", crd.Name, "hasValues", crd.Spec.Release.Values != nil) + if err := r.updateHelmReleasesForCRD(ctx, crd); err != nil { + logger.Error(err, "failed to update HelmReleases for CRD", "crd", crd.Name) + // Continue with other CRDs even if one fails + } + } + + // Continue with debounced restart logic + return r.debouncedRestart(ctx) +} + +func (r *ApplicationDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error { + if r.Debounce == 0 { + r.Debounce = 5 * time.Second + } + + return ctrl.NewControllerManagedBy(mgr). + Named("applicationdefinition-controller"). + Watches( + &cozyv1alpha1.ApplicationDefinition{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + r.mu.Lock() + r.lastEvent = time.Now() + r.mu.Unlock() + return []reconcile.Request{{ + NamespacedName: types.NamespacedName{ + Namespace: "cozy-system", + Name: "cozystack-api", + }, + }} + }), + ). + Watches( + &helmv2.HelmRelease{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + hr, ok := obj.(*helmv2.HelmRelease) + if !ok { + return nil + } + // Only watch HelmReleases with cozystack.io/ui=true label + if hr.Labels == nil || hr.Labels["cozystack.io/ui"] != "true" { + return nil + } + // Trigger reconciliation of all CRDs when a HelmRelease with the label is created/updated + r.mu.Lock() + r.lastEvent = time.Now() + r.mu.Unlock() + return []reconcile.Request{{ + NamespacedName: types.NamespacedName{ + Namespace: "cozy-system", + Name: "cozystack-api", + }, + }} + }), + ). + Complete(r) +} + +type crdHashView struct { + Name string `json:"name"` + Spec cozyv1alpha1.ApplicationDefinitionSpec `json:"spec"` +} + +func (r *ApplicationDefinitionReconciler) computeConfigHash(ctx context.Context) (string, error) { + list := &cozyv1alpha1.ApplicationDefinitionList{} + if err := r.List(ctx, list); err != nil { + return "", err + } + + slices.SortFunc(list.Items, sortApplicationDefinitions) + + views := make([]crdHashView, 0, len(list.Items)) + for i := range list.Items { + views = append(views, crdHashView{ + Name: list.Items[i].Name, + Spec: list.Items[i].Spec, + }) + } + b, err := json.Marshal(views) + if err != nil { + return "", err + } + sum := sha256.Sum256(b) + return hex.EncodeToString(sum[:]), nil +} + +func (r *ApplicationDefinitionReconciler) debouncedRestart(ctx context.Context) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + r.mu.Lock() + le := r.lastEvent + lh := r.lastHandled + debounce := r.Debounce + r.mu.Unlock() + + if debounce <= 0 { + debounce = 5 * time.Second + } + if le.IsZero() { + return ctrl.Result{}, nil + } + if d := time.Since(le); d < debounce { + return ctrl.Result{RequeueAfter: debounce - d}, nil + } + if !lh.Before(le) { + return ctrl.Result{}, nil + } + + newHash, err := r.computeConfigHash(ctx) + if err != nil { + return ctrl.Result{}, err + } + + tpl, obj, patch, err := r.getWorkload(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"}) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + oldHash := tpl.Annotations["cozystack.io/config-hash"] + + if oldHash == newHash && oldHash != "" { + r.mu.Lock() + r.lastHandled = le + r.mu.Unlock() + logger.Info("No changes in CRD config; skipping restart", "hash", newHash) + return ctrl.Result{}, nil + } + + tpl.Annotations["cozystack.io/config-hash"] = newHash + + if err := r.Patch(ctx, obj, patch); err != nil { + return ctrl.Result{}, err + } + + r.mu.Lock() + r.lastHandled = le + r.mu.Unlock() + + logger.Info("Updated cozystack-api podTemplate config-hash; rollout triggered", + "old", oldHash, "new", newHash) + return ctrl.Result{}, nil +} + +func (r *ApplicationDefinitionReconciler) getWorkload( + ctx context.Context, + key types.NamespacedName, +) (tpl *corev1.PodTemplateSpec, obj client.Object, patch client.Patch, err error) { + if r.CozystackAPIKind == "Deployment" { + dep := &appsv1.Deployment{} + if err := r.Get(ctx, key, dep); err != nil { + return nil, nil, nil, err + } + obj = dep + tpl = &dep.Spec.Template + patch = client.MergeFrom(dep.DeepCopy()) + } else { + ds := &appsv1.DaemonSet{} + if err := r.Get(ctx, key, ds); err != nil { + return nil, nil, nil, err + } + obj = ds + tpl = &ds.Spec.Template + patch = client.MergeFrom(ds.DeepCopy()) + } + if tpl.Annotations == nil { + tpl.Annotations = make(map[string]string) + } + return tpl, obj, patch, nil +} + +func sortApplicationDefinitions(a, b cozyv1alpha1.ApplicationDefinition) int { + if a.Name == b.Name { + return 0 + } + if a.Name < b.Name { + return -1 + } + return 1 +} + +// updateHelmReleasesForCRD updates all HelmReleases that match the application labels from ApplicationDefinition +func (r *ApplicationDefinitionReconciler) updateHelmReleasesForCRD(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { + logger := log.FromContext(ctx) + + // Use application labels to find HelmReleases + // Labels: apps.cozystack.io/application.kind and apps.cozystack.io/application.group + applicationKind := crd.Spec.Application.Kind + applicationGroup := "apps.cozystack.io" // All applications use this group + + // Build label selector for HelmReleases + // Only reconcile HelmReleases with cozystack.io/ui=true label + labelSelector := client.MatchingLabels{ + "apps.cozystack.io/application.kind": applicationKind, + "apps.cozystack.io/application.group": applicationGroup, + "cozystack.io/ui": "true", + } + + // List all HelmReleases with matching labels + hrList := &helmv2.HelmReleaseList{} + if err := r.List(ctx, hrList, labelSelector); err != nil { + logger.Error(err, "failed to list HelmReleases", "kind", applicationKind, "group", applicationGroup) + return err + } + + logger.Info("Found HelmReleases to update", "crd", crd.Name, "kind", applicationKind, "count", len(hrList.Items), "hasValues", crd.Spec.Release.Values != nil) + if crd.Spec.Release.Values != nil { + logger.V(4).Info("CRD has values", "crd", crd.Name, "valuesSize", len(crd.Spec.Release.Values.Raw)) + } + + // Log each HelmRelease that will be updated + for i := range hrList.Items { + hr := &hrList.Items[i] + logger.V(4).Info("Processing HelmRelease", "name", hr.Name, "namespace", hr.Namespace, "kind", applicationKind) + } + + // Update each HelmRelease + for i := range hrList.Items { + hr := &hrList.Items[i] + if err := r.updateHelmReleaseChart(ctx, hr, crd); err != nil { + logger.Error(err, "failed to update HelmRelease", "name", hr.Name, "namespace", hr.Namespace) + continue + } + } + + return nil +} + +// updateHelmReleaseChart updates the chart/chartRef and values in HelmRelease based on ApplicationDefinition +func (r *ApplicationDefinitionReconciler) updateHelmReleaseChart(ctx context.Context, hr *helmv2.HelmRelease, crd *cozyv1alpha1.ApplicationDefinition) error { + logger := log.FromContext(ctx) + updated := false + hrCopy := hr.DeepCopy() + + // Update based on Chart or ChartRef configuration + if crd.Spec.Release.Chart != nil { + // Using Chart (HelmRepository) + if hrCopy.Spec.Chart == nil { + // Need to create Chart spec + hrCopy.Spec.Chart = &helmv2.HelmChartTemplate{ + Spec: helmv2.HelmChartTemplateSpec{ + Chart: crd.Spec.Release.Chart.Name, + SourceRef: helmv2.CrossNamespaceObjectReference{ + Kind: crd.Spec.Release.Chart.SourceRef.Kind, + Name: crd.Spec.Release.Chart.SourceRef.Name, + Namespace: crd.Spec.Release.Chart.SourceRef.Namespace, + }, + }, + } + // Clear ChartRef if it exists + hrCopy.Spec.ChartRef = nil + updated = true + } else { + // Update existing Chart spec + if hrCopy.Spec.Chart.Spec.Chart != crd.Spec.Release.Chart.Name || + hrCopy.Spec.Chart.Spec.SourceRef.Kind != crd.Spec.Release.Chart.SourceRef.Kind || + hrCopy.Spec.Chart.Spec.SourceRef.Name != crd.Spec.Release.Chart.SourceRef.Name || + hrCopy.Spec.Chart.Spec.SourceRef.Namespace != crd.Spec.Release.Chart.SourceRef.Namespace { + hrCopy.Spec.Chart.Spec.Chart = crd.Spec.Release.Chart.Name + hrCopy.Spec.Chart.Spec.SourceRef = helmv2.CrossNamespaceObjectReference{ + Kind: crd.Spec.Release.Chart.SourceRef.Kind, + Name: crd.Spec.Release.Chart.SourceRef.Name, + Namespace: crd.Spec.Release.Chart.SourceRef.Namespace, + } + // Clear ChartRef if it exists + hrCopy.Spec.ChartRef = nil + updated = true + } + } + } else if crd.Spec.Release.ChartRef != nil { + // Using ChartRef (ExternalArtifact) + expectedChartRef := &helmv2.CrossNamespaceSourceReference{ + Kind: "ExternalArtifact", + Name: crd.Spec.Release.ChartRef.SourceRef.Name, + Namespace: crd.Spec.Release.ChartRef.SourceRef.Namespace, + } + + if hrCopy.Spec.ChartRef == nil { + // Need to create ChartRef + hrCopy.Spec.ChartRef = expectedChartRef + // Clear Chart if it exists + hrCopy.Spec.Chart = nil + updated = true + } else { + // Update existing ChartRef + if hrCopy.Spec.ChartRef.Kind != expectedChartRef.Kind || + hrCopy.Spec.ChartRef.Name != expectedChartRef.Name || + hrCopy.Spec.ChartRef.Namespace != expectedChartRef.Namespace { + hrCopy.Spec.ChartRef = expectedChartRef + // Clear Chart if it exists + hrCopy.Spec.Chart = nil + updated = true + } + } + } + + // Update Values from CRD if specified + var mergedValues *apiextensionsv1.JSON + var err error + if crd.Spec.Release.Values != nil { + logger.V(4).Info("Merging values from CRD", "name", hr.Name, "namespace", hr.Namespace, "crd", crd.Name) + mergedValues, err = cozylib.MergeValuesWithCRDPriority(crd.Spec.Release.Values, hrCopy.Spec.Values) + if err != nil { + logger.Error(err, "failed to merge values", "name", hr.Name, "namespace", hr.Namespace) + return fmt.Errorf("failed to merge values: %w", err) + } + } else { + // Even if CRD has no values, we still need to ensure _namespace is set + mergedValues = hrCopy.Spec.Values + } + + // Always inject namespace annotations (top-level _namespace field) + // This matches the behavior in cozystack-api and NamespaceHelmReconciler + namespace := &corev1.Namespace{} + if err := r.Get(ctx, client.ObjectKey{Name: hrCopy.Namespace}, namespace); err == nil { + mergedValues, err = cozylib.InjectNamespaceAnnotationsIntoValues(mergedValues, namespace) + if err != nil { + logger.Error(err, "failed to inject namespace annotations", "name", hr.Name, "namespace", hr.Namespace) + // Continue even if namespace annotations injection fails + } + } + + // Always update values to ensure _cozystack and _namespace are applied + // This ensures that CRD values (especially _cozystack and _namespace) are always applied + // We always update to ensure CRD values are propagated, even if they appear equal + // This is important because JSON comparison might not catch all differences (e.g., field order) + if crd.Spec.Release.Values != nil || mergedValues != hrCopy.Spec.Values { + hrCopy.Spec.Values = mergedValues + updated = true + if crd.Spec.Release.Values != nil { + logger.Info("Updated values from CRD", "name", hr.Name, "namespace", hr.Namespace, "crd", crd.Name) + } else { + logger.V(4).Info("Updated values with namespace labels", "name", hr.Name, "namespace", hr.Namespace, "crd", crd.Name) + } + } else { + logger.V(4).Info("No values update needed", "name", hr.Name, "namespace", hr.Namespace, "crd", crd.Name) + } + + if !updated { + return nil + } + + // Update the HelmRelease + patch := client.MergeFrom(hr.DeepCopy()) + if err := r.Patch(ctx, hrCopy, patch); err != nil { + return err + } + + logger.Info("Updated HelmRelease", "name", hr.Name, "namespace", hr.Namespace, "crd", crd.Name) + return nil +} + +// mergeHelmReleaseValues merges CRD default values with existing HelmRelease values +// All fields are merged except "_cozystack" and "_namespace" which are fully overwritten from CRD values +// Existing HelmRelease values (outside of _cozystack and _namespace) take precedence (user values override defaults) +func (r *ApplicationDefinitionReconciler) mergeHelmReleaseValues(crdValues, existingValues *apiextensionsv1.JSON) (*apiextensionsv1.JSON, error) { + // If CRD has no values, preserve existing + if crdValues == nil || len(crdValues.Raw) == 0 { + return existingValues, nil + } + + // If existing has no values, use CRD values + if existingValues == nil || len(existingValues.Raw) == 0 { + return crdValues, nil + } + + var crdMap, existingMap map[string]interface{} + + // Parse CRD values (defaults) + if err := json.Unmarshal(crdValues.Raw, &crdMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal CRD values: %w", err) + } + + // Parse existing HelmRelease values + if err := json.Unmarshal(existingValues.Raw, &existingMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal existing values: %w", err) + } + + // Start with existing values as base (user values take priority) + // Then merge CRD values on top, but _cozystack and _namespace from CRD completely overwrite + merged := deepMergeMaps(existingMap, crdMap) + + // Explicitly handle "_cozystack" field: CRD values completely overwrite existing + // This ensures _cozystack field from CRD is always used, even if user modified it + if crdCozystack, exists := crdMap["_cozystack"]; exists { + merged["_cozystack"] = crdCozystack + } + + // Explicitly handle "_namespace" field: CRD values completely overwrite existing + // This ensures _namespace field from CRD is always used, even if user modified it + if crdNamespace, exists := crdMap["_namespace"]; exists { + merged["_namespace"] = crdNamespace + } + + mergedJSON, err := json.Marshal(merged) + if err != nil { + return nil, fmt.Errorf("failed to marshal merged values: %w", err) + } + + return &apiextensionsv1.JSON{Raw: mergedJSON}, nil +} + +// deepMergeMaps performs a deep merge of two maps +func deepMergeMaps(base, override map[string]interface{}) map[string]interface{} { + result := make(map[string]interface{}) + + // Copy base map + for k, v := range base { + result[k] = v + } + + // Merge override map + for k, v := range override { + if baseVal, exists := result[k]; exists { + // If both are maps, recursively merge + if baseMap, ok := baseVal.(map[string]interface{}); ok { + if overrideMap, ok := v.(map[string]interface{}); ok { + result[k] = deepMergeMaps(baseMap, overrideMap) + continue + } + } + } + // Override takes precedence + result[k] = v + } + + return result +} + +// valuesEqual compares two JSON values for equality +func valuesEqual(a, b *apiextensionsv1.JSON) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil { + return false + } + // Simple byte comparison (could be improved with canonical JSON) + return string(a.Raw) == string(b.Raw) +} + diff --git a/internal/controller/cozystackresource_controller.go b/internal/controller/cozystackresource_controller.go deleted file mode 100644 index 46884418..00000000 --- a/internal/controller/cozystackresource_controller.go +++ /dev/null @@ -1,187 +0,0 @@ -package controller - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "slices" - "sync" - "time" - - cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -type CozystackResourceDefinitionReconciler struct { - client.Client - Scheme *runtime.Scheme - - Debounce time.Duration - - mu sync.Mutex - lastEvent time.Time - lastHandled time.Time - - CozystackAPIKind string -} - -func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - return r.debouncedRestart(ctx) -} - -func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error { - if r.Debounce == 0 { - r.Debounce = 5 * time.Second - } - - return ctrl.NewControllerManagedBy(mgr). - Named("cozystackresource-controller"). - Watches( - &cozyv1alpha1.CozystackResourceDefinition{}, - handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { - r.mu.Lock() - r.lastEvent = time.Now() - r.mu.Unlock() - return []reconcile.Request{{ - NamespacedName: types.NamespacedName{ - Namespace: "cozy-system", - Name: "cozystack-api", - }, - }} - }), - ). - Complete(r) -} - -type crdHashView struct { - Name string `json:"name"` - Spec cozyv1alpha1.CozystackResourceDefinitionSpec `json:"spec"` -} - -func (r *CozystackResourceDefinitionReconciler) computeConfigHash(ctx context.Context) (string, error) { - list := &cozyv1alpha1.CozystackResourceDefinitionList{} - if err := r.List(ctx, list); err != nil { - return "", err - } - - slices.SortFunc(list.Items, sortCozyRDs) - - views := make([]crdHashView, 0, len(list.Items)) - for i := range list.Items { - views = append(views, crdHashView{ - Name: list.Items[i].Name, - Spec: list.Items[i].Spec, - }) - } - b, err := json.Marshal(views) - if err != nil { - return "", err - } - sum := sha256.Sum256(b) - return hex.EncodeToString(sum[:]), nil -} - -func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Context) (ctrl.Result, error) { - logger := log.FromContext(ctx) - - r.mu.Lock() - le := r.lastEvent - lh := r.lastHandled - debounce := r.Debounce - r.mu.Unlock() - - if debounce <= 0 { - debounce = 5 * time.Second - } - if le.IsZero() { - return ctrl.Result{}, nil - } - if d := time.Since(le); d < debounce { - return ctrl.Result{RequeueAfter: debounce - d}, nil - } - if !lh.Before(le) { - return ctrl.Result{}, nil - } - - newHash, err := r.computeConfigHash(ctx) - if err != nil { - return ctrl.Result{}, err - } - - tpl, obj, patch, err := r.getWorkload(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"}) - if err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) - } - - oldHash := tpl.Annotations["cozystack.io/config-hash"] - - if oldHash == newHash && oldHash != "" { - r.mu.Lock() - r.lastHandled = le - r.mu.Unlock() - logger.Info("No changes in CRD config; skipping restart", "hash", newHash) - return ctrl.Result{}, nil - } - - tpl.Annotations["cozystack.io/config-hash"] = newHash - - if err := r.Patch(ctx, obj, patch); err != nil { - return ctrl.Result{}, err - } - - r.mu.Lock() - r.lastHandled = le - r.mu.Unlock() - - logger.Info("Updated cozystack-api podTemplate config-hash; rollout triggered", - "old", oldHash, "new", newHash) - return ctrl.Result{}, nil -} - -func (r *CozystackResourceDefinitionReconciler) getWorkload( - ctx context.Context, - key types.NamespacedName, -) (tpl *corev1.PodTemplateSpec, obj client.Object, patch client.Patch, err error) { - if r.CozystackAPIKind == "Deployment" { - dep := &appsv1.Deployment{} - if err := r.Get(ctx, key, dep); err != nil { - return nil, nil, nil, err - } - obj = dep - tpl = &dep.Spec.Template - patch = client.MergeFrom(dep.DeepCopy()) - } else { - ds := &appsv1.DaemonSet{} - if err := r.Get(ctx, key, ds); err != nil { - return nil, nil, nil, err - } - obj = ds - tpl = &ds.Spec.Template - patch = client.MergeFrom(ds.DeepCopy()) - } - if tpl.Annotations == nil { - tpl.Annotations = make(map[string]string) - } - return tpl, obj, patch, nil -} - -func sortCozyRDs(a, b cozyv1alpha1.CozystackResourceDefinition) int { - if a.Name == b.Name { - return 0 - } - if a.Name < b.Name { - return -1 - } - return 1 -} diff --git a/internal/controller/dashboard/breadcrumb.go b/internal/controller/dashboard/breadcrumb.go index 5122f605..aadcacac 100644 --- a/internal/controller/dashboard/breadcrumb.go +++ b/internal/controller/dashboard/breadcrumb.go @@ -14,7 +14,7 @@ import ( ) // ensureBreadcrumb creates or updates a Breadcrumb resource for the given CRD -func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { group, version, kind := pickGVK(crd) lowerKind := strings.ToLower(kind) diff --git a/internal/controller/dashboard/customcolumns.go b/internal/controller/dashboard/customcolumns.go index 6c23d68b..be1318f8 100644 --- a/internal/controller/dashboard/customcolumns.go +++ b/internal/controller/dashboard/customcolumns.go @@ -21,7 +21,7 @@ import ( // // metadata.name: stock-namespace-.. // spec.id: stock-namespace-/// -func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (controllerutil.OperationResult, error) { +func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) (controllerutil.OperationResult, error) { g, v, kind := pickGVK(crd) plural := pickPlural(kind, crd) // Details page segment uses lowercase kind, mirroring your example diff --git a/internal/controller/dashboard/customformsoverride.go b/internal/controller/dashboard/customformsoverride.go index f88202bc..dee37d05 100644 --- a/internal/controller/dashboard/customformsoverride.go +++ b/internal/controller/dashboard/customformsoverride.go @@ -15,7 +15,7 @@ import ( ) // ensureCustomFormsOverride creates or updates a CustomFormsOverride resource for the given CRD -func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { g, v, kind := pickGVK(crd) plural := pickPlural(kind, crd) diff --git a/internal/controller/dashboard/customformsprefill.go b/internal/controller/dashboard/customformsprefill.go index d2761873..35d22ff1 100644 --- a/internal/controller/dashboard/customformsprefill.go +++ b/internal/controller/dashboard/customformsprefill.go @@ -16,7 +16,7 @@ import ( ) // ensureCustomFormsPrefill creates or updates a CustomFormsPrefill resource for the given CRD -func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) { +func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) (reconcile.Result, error) { logger := log.FromContext(ctx) app := crd.Spec.Application diff --git a/internal/controller/dashboard/factory.go b/internal/controller/dashboard/factory.go index 53d771d0..84578dd0 100644 --- a/internal/controller/dashboard/factory.go +++ b/internal/controller/dashboard/factory.go @@ -15,7 +15,7 @@ import ( ) // ensureFactory creates or updates a Factory resource for the given CRD -func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { g, v, kind := pickGVK(crd) plural := pickPlural(kind, crd) @@ -557,7 +557,7 @@ type factoryFlags struct { // factoryFeatureFlags tries several conventional locations so you can evolve the API // without breaking the controller. Defaults are false (hidden). -func factoryFeatureFlags(crd *cozyv1alpha1.CozystackResourceDefinition) factoryFlags { +func factoryFeatureFlags(crd *cozyv1alpha1.ApplicationDefinition) factoryFlags { var f factoryFlags f.Workloads = true diff --git a/internal/controller/dashboard/helpers.go b/internal/controller/dashboard/helpers.go index a0023a05..2a35bbff 100644 --- a/internal/controller/dashboard/helpers.go +++ b/internal/controller/dashboard/helpers.go @@ -23,7 +23,7 @@ type fieldInfo struct { // pickGVK tries to read group/version/kind from the CRD. We prefer the "application" section, // falling back to other likely fields if your schema differs. -func pickGVK(crd *cozyv1alpha1.CozystackResourceDefinition) (group, version, kind string) { +func pickGVK(crd *cozyv1alpha1.ApplicationDefinition) (group, version, kind string) { // Best guess based on your examples: if crd.Spec.Application.Kind != "" { kind = crd.Spec.Application.Kind @@ -41,7 +41,7 @@ func pickGVK(crd *cozyv1alpha1.CozystackResourceDefinition) (group, version, kin } // pickPlural prefers a field on the CRD if you have it; otherwise do a simple lowercase + "s". -func pickPlural(kind string, crd *cozyv1alpha1.CozystackResourceDefinition) string { +func pickPlural(kind string, crd *cozyv1alpha1.ApplicationDefinition) string { // If you have crd.Spec.Application.Plural, prefer it. Example: if crd.Spec.Application.Plural != "" { return crd.Spec.Application.Plural diff --git a/internal/controller/dashboard/manager.go b/internal/controller/dashboard/manager.go index 12e50fbc..10da06bd 100644 --- a/internal/controller/dashboard/manager.go +++ b/internal/controller/dashboard/manager.go @@ -56,7 +56,7 @@ func NewManager(c client.Client, scheme *runtime.Scheme) *Manager { func (m *Manager) SetupWithManager(mgr ctrl.Manager) error { if err := ctrl.NewControllerManagedBy(mgr). Named("dashboard-reconciler"). - For(&cozyv1alpha1.CozystackResourceDefinition{}). + For(&cozyv1alpha1.ApplicationDefinition{}). Complete(m); err != nil { return err } @@ -72,7 +72,7 @@ func (m *Manager) SetupWithManager(mgr ctrl.Manager) error { func (m *Manager) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { l := log.FromContext(ctx) - crd := &cozyv1alpha1.CozystackResourceDefinition{} + crd := &cozyv1alpha1.ApplicationDefinition{} err := m.Get(ctx, types.NamespacedName{Name: req.Name}, crd) if err != nil { @@ -99,7 +99,7 @@ func (m *Manager) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, // - ensureMarketplacePanel (implemented) // - ensureSidebar (implemented) // - ensureTableUriMapping (implemented) -func (m *Manager) EnsureForCRD(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) { +func (m *Manager) EnsureForCRD(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) (reconcile.Result, error) { // Early return if crd.Spec.Dashboard is nil to prevent oscillation if crd.Spec.Dashboard == nil { return reconcile.Result{}, nil @@ -148,7 +148,7 @@ func (m *Manager) InitializeStaticResources(ctx context.Context) error { } // addDashboardLabels adds standard dashboard management labels to a resource -func (m *Manager) addDashboardLabels(obj client.Object, crd *cozyv1alpha1.CozystackResourceDefinition, resourceType string) { +func (m *Manager) addDashboardLabels(obj client.Object, crd *cozyv1alpha1.ApplicationDefinition, resourceType string) { labels := obj.GetLabels() if labels == nil { labels = make(map[string]string) @@ -197,7 +197,7 @@ func (m *Manager) getStaticResourceSelector() client.MatchingLabels { // CleanupOrphanedResources removes dashboard resources that are no longer needed // This should be called after cache warming to ensure all current resources are known func (m *Manager) CleanupOrphanedResources(ctx context.Context) error { - var crdList cozyv1alpha1.CozystackResourceDefinitionList + var crdList cozyv1alpha1.ApplicationDefinitionList if err := m.List(ctx, &crdList, &client.ListOptions{}); err != nil { return err } @@ -228,7 +228,7 @@ func (m *Manager) CleanupOrphanedResources(ctx context.Context) error { } // buildExpectedResourceSet creates a map of expected resource names by type -func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.CozystackResourceDefinition) map[string]map[string]bool { +func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.ApplicationDefinition) map[string]map[string]bool { expected := make(map[string]map[string]bool) // Initialize maps for each resource type diff --git a/internal/controller/dashboard/marketplacepanel.go b/internal/controller/dashboard/marketplacepanel.go index 82a8f336..6bfce752 100644 --- a/internal/controller/dashboard/marketplacepanel.go +++ b/internal/controller/dashboard/marketplacepanel.go @@ -16,7 +16,7 @@ import ( ) // ensureMarketplacePanel creates or updates a MarketplacePanel resource for the given CRD -func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) { +func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) (reconcile.Result, error) { logger := log.FromContext(ctx) mp := &dashv1alpha1.MarketplacePanel{} diff --git a/internal/controller/dashboard/sidebar.go b/internal/controller/dashboard/sidebar.go index 97937921..b10cf5a2 100644 --- a/internal/controller/dashboard/sidebar.go +++ b/internal/controller/dashboard/sidebar.go @@ -28,12 +28,12 @@ import ( // - Categories are ordered strictly as: // Marketplace, IaaS, PaaS, NaaS, , Resources, Administration // - Items within each category: sort by Weight (desc), then Label (A→Z). -func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { // Build the full menu once. // 1) Fetch all CRDs - var all []cozyv1alpha1.CozystackResourceDefinition - var crdList cozyv1alpha1.CozystackResourceDefinitionList + var all []cozyv1alpha1.ApplicationDefinition + var crdList cozyv1alpha1.ApplicationDefinitionList if err := m.List(ctx, &crdList, &client.ListOptions{}); err != nil { return err } @@ -228,7 +228,7 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Cozystack // upsertMultipleSidebars creates/updates several Sidebar resources with the same menu spec. func (m *Manager) upsertMultipleSidebars( ctx context.Context, - crd *cozyv1alpha1.CozystackResourceDefinition, + crd *cozyv1alpha1.ApplicationDefinition, ids []string, keysAndTags map[string]any, menuItems []any, @@ -335,7 +335,7 @@ func orderCategoryLabels[T any](cats map[string][]T) []string { } // safeCategory returns spec.dashboard.category or "Resources" if not set. -func safeCategory(def *cozyv1alpha1.CozystackResourceDefinition) string { +func safeCategory(def *cozyv1alpha1.ApplicationDefinition) string { if def == nil || def.Spec.Dashboard == nil { return "Resources" } diff --git a/internal/controller/dashboard/tableurimapping.go b/internal/controller/dashboard/tableurimapping.go index 6e8a395d..e9a4849c 100644 --- a/internal/controller/dashboard/tableurimapping.go +++ b/internal/controller/dashboard/tableurimapping.go @@ -7,7 +7,7 @@ import ( ) // ensureTableUriMapping creates or updates a TableUriMapping resource for the given CRD -func (m *Manager) ensureTableUriMapping(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureTableUriMapping(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { // Links are fully managed by the CustomColumnsOverride. return nil } diff --git a/internal/controller/namespace_helm_reconciler.go b/internal/controller/namespace_helm_reconciler.go new file mode 100644 index 00000000..57719ddd --- /dev/null +++ b/internal/controller/namespace_helm_reconciler.go @@ -0,0 +1,170 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "encoding/json" + "fmt" + + helmv2 "github.com/fluxcd/helm-controller/api/v2" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/cozystack/cozystack/pkg/cozylib" +) + +// +kubebuilder:rbac:groups=core,resources=namespaces,verbs=get;list;watch +// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch;update;patch +type NamespaceHelmReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// Reconcile processes namespace changes and updates HelmReleases with namespace labels +func (r *NamespaceHelmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + // Get the namespace + namespace := &corev1.Namespace{} + if err := r.Get(ctx, req.NamespacedName, namespace); err != nil { + logger.Error(err, "unable to fetch Namespace") + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // Extract namespace.cozystack.io/* annotations + namespaceLabels := cozylib.ExtractNamespaceAnnotations(namespace) + if len(namespaceLabels) == 0 { + // No namespace labels to process, skip + return ctrl.Result{}, nil + } + + logger.Info("processing namespace labels", "namespace", namespace.Name, "labels", namespaceLabels) + + // List all HelmReleases in this namespace + helmReleaseList := &helmv2.HelmReleaseList{} + if err := r.List(ctx, helmReleaseList, client.InNamespace(namespace.Name)); err != nil { + logger.Error(err, "unable to list HelmReleases in namespace", "namespace", namespace.Name) + return ctrl.Result{}, err + } + + // Update each HelmRelease with namespace labels + updated := 0 + for i := range helmReleaseList.Items { + hr := &helmReleaseList.Items[i] + if err := r.updateHelmReleaseWithNamespaceLabels(ctx, hr, namespaceLabels); err != nil { + logger.Error(err, "failed to update HelmRelease", "name", hr.Name, "namespace", hr.Namespace) + continue + } + updated++ + } + + if updated > 0 { + logger.Info("updated HelmReleases with namespace labels", "namespace", namespace.Name, "count", updated) + } + + return ctrl.Result{}, nil +} + + +// updateHelmReleaseWithNamespaceLabels updates HelmRelease values with namespace labels +func (r *NamespaceHelmReconciler) updateHelmReleaseWithNamespaceLabels(ctx context.Context, hr *helmv2.HelmRelease, namespaceLabels map[string]string) error { + logger := log.FromContext(ctx) + + // Parse current values + var valuesMap map[string]interface{} + if hr.Spec.Values != nil && len(hr.Spec.Values.Raw) > 0 { + if err := json.Unmarshal(hr.Spec.Values.Raw, &valuesMap); err != nil { + return fmt.Errorf("failed to unmarshal HelmRelease values: %w", err) + } + } else { + valuesMap = make(map[string]interface{}) + } + + // Convert namespaceLabels from map[string]string to map[string]interface{} + namespaceLabelsMap := make(map[string]interface{}) + for k, v := range namespaceLabels { + namespaceLabelsMap[k] = v + } + + // Check if namespace labels need to be updated (top-level _namespace field) + needsUpdate := false + currentNamespace, exists := valuesMap["_namespace"] + if !exists { + needsUpdate = true + valuesMap["_namespace"] = namespaceLabelsMap + } else { + currentNamespaceMap, ok := currentNamespace.(map[string]interface{}) + if !ok { + needsUpdate = true + valuesMap["_namespace"] = namespaceLabelsMap + } else { + // Compare and update if different + for k, v := range namespaceLabelsMap { + if currentVal, exists := currentNamespaceMap[k]; !exists || currentVal != v { + needsUpdate = true + currentNamespaceMap[k] = v + } + } + // Remove keys that are no longer in namespace labels + for k := range currentNamespaceMap { + if _, exists := namespaceLabelsMap[k]; !exists { + needsUpdate = true + delete(currentNamespaceMap, k) + } + } + if needsUpdate { + valuesMap["_namespace"] = currentNamespaceMap + } + } + } + + if !needsUpdate { + // No changes needed + return nil + } + + // Marshal back to JSON + mergedJSON, err := json.Marshal(valuesMap) + if err != nil { + return fmt.Errorf("failed to marshal values with namespace labels: %w", err) + } + + // Update HelmRelease + patchTarget := hr.DeepCopy() + patchTarget.Spec.Values = &apiextensionsv1.JSON{Raw: mergedJSON} + + patch := client.MergeFrom(hr) + if err := r.Patch(ctx, patchTarget, patch); err != nil { + return fmt.Errorf("failed to patch HelmRelease: %w", err) + } + + logger.Info("updated HelmRelease with namespace labels", "name", hr.Name, "namespace", hr.Namespace) + return nil +} + +// SetupWithManager sets up the controller with the Manager +func (r *NamespaceHelmReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&corev1.Namespace{}). + Complete(r) +} + diff --git a/internal/controller/system_helm_reconciler.go b/internal/controller/system_helm_reconciler.go deleted file mode 100644 index 6e40027c..00000000 --- a/internal/controller/system_helm_reconciler.go +++ /dev/null @@ -1,140 +0,0 @@ -package controller - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "sort" - "time" - - helmv2 "github.com/fluxcd/helm-controller/api/v2" - corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/predicate" -) - -type CozystackConfigReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -var configMapNames = []string{"cozystack", "cozystack-branding", "cozystack-scheduling"} - -const configMapNamespace = "cozy-system" -const digestAnnotation = "cozystack.io/cozy-config-digest" -const forceReconcileKey = "reconcile.fluxcd.io/forceAt" -const requestedAt = "reconcile.fluxcd.io/requestedAt" - -func (r *CozystackConfigReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) { - log := log.FromContext(ctx) - time.Sleep(2 * time.Second) - - digest, err := r.computeDigest(ctx) - if err != nil { - log.Error(err, "failed to compute config digest") - return ctrl.Result{}, nil - } - - var helmList helmv2.HelmReleaseList - if err := r.List(ctx, &helmList); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to list HelmReleases: %w", err) - } - - now := time.Now().Format(time.RFC3339Nano) - updated := 0 - - for _, hr := range helmList.Items { - isSystemApp := hr.Labels["cozystack.io/system-app"] == "true" - isTenantRoot := hr.Namespace == "tenant-root" && hr.Name == "tenant-root" - if !isSystemApp && !isTenantRoot { - continue - } - patchTarget := hr.DeepCopy() - - if hr.Annotations == nil { - hr.Annotations = map[string]string{} - } - - if hr.Annotations[digestAnnotation] == digest { - continue - } - patchTarget.Annotations[digestAnnotation] = digest - patchTarget.Annotations[forceReconcileKey] = now - patchTarget.Annotations[requestedAt] = now - - patch := client.MergeFrom(hr.DeepCopy()) - if err := r.Patch(ctx, patchTarget, patch); err != nil { - log.Error(err, "failed to patch HelmRelease", "name", hr.Name, "namespace", hr.Namespace) - continue - } - updated++ - log.Info("patched HelmRelease with new config digest", "name", hr.Name, "namespace", hr.Namespace) - } - - log.Info("finished reconciliation", "updatedHelmReleases", updated) - return ctrl.Result{}, nil -} - -func (r *CozystackConfigReconciler) computeDigest(ctx context.Context) (string, error) { - hash := sha256.New() - - for _, name := range configMapNames { - var cm corev1.ConfigMap - err := r.Get(ctx, client.ObjectKey{Namespace: configMapNamespace, Name: name}, &cm) - if err != nil { - if kerrors.IsNotFound(err) { - continue // ignore missing - } - return "", err - } - - // Sort keys for consistent hashing - var keys []string - for k := range cm.Data { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - v := cm.Data[k] - fmt.Fprintf(hash, "%s:%s=%s\n", name, k, v) - } - } - - return hex.EncodeToString(hash.Sum(nil)), nil -} - -func (r *CozystackConfigReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - WithEventFilter(predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - cm, ok := e.ObjectNew.(*corev1.ConfigMap) - return ok && cm.Namespace == configMapNamespace && contains(configMapNames, cm.Name) - }, - CreateFunc: func(e event.CreateEvent) bool { - cm, ok := e.Object.(*corev1.ConfigMap) - return ok && cm.Namespace == configMapNamespace && contains(configMapNames, cm.Name) - }, - DeleteFunc: func(e event.DeleteEvent) bool { - cm, ok := e.Object.(*corev1.ConfigMap) - return ok && cm.Namespace == configMapNamespace && contains(configMapNames, cm.Name) - }, - }). - For(&corev1.ConfigMap{}). - Complete(r) -} - -func contains(slice []string, val string) bool { - for _, s := range slice { - if s == val { - return true - } - } - return false -} diff --git a/internal/controller/tenant_helm_reconciler.go b/internal/controller/tenant_helm_reconciler.go deleted file mode 100644 index 28b4ad16..00000000 --- a/internal/controller/tenant_helm_reconciler.go +++ /dev/null @@ -1,159 +0,0 @@ -package controller - -import ( - "context" - "fmt" - "strings" - "time" - - e "errors" - - helmv2 "github.com/fluxcd/helm-controller/api/v2" - "gopkg.in/yaml.v2" - corev1 "k8s.io/api/core/v1" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" -) - -type TenantHelmReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -func (r *TenantHelmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - logger := log.FromContext(ctx) - time.Sleep(2 * time.Second) - - hr := &helmv2.HelmRelease{} - if err := r.Get(ctx, req.NamespacedName, hr); err != nil { - if errors.IsNotFound(err) { - return ctrl.Result{}, nil - } - logger.Error(err, "unable to fetch HelmRelease") - return ctrl.Result{}, err - } - - if !strings.HasPrefix(hr.Name, "tenant-") { - return ctrl.Result{}, nil - } - - if len(hr.Status.Conditions) == 0 || hr.Status.Conditions[0].Type != "Ready" { - return ctrl.Result{}, nil - } - - if len(hr.Status.History) == 0 { - logger.Info("no history in HelmRelease status", "name", hr.Name) - return ctrl.Result{}, nil - } - - if hr.Status.History[0].Status != "deployed" { - return ctrl.Result{}, nil - } - - newDigest := hr.Status.History[0].Digest - var hrList helmv2.HelmReleaseList - childNamespace := getChildNamespace(hr.Namespace, hr.Name) - if childNamespace == "tenant-root" && hr.Name == "tenant-root" { - if hr.Spec.Values == nil { - logger.Error(e.New("hr.Spec.Values is nil"), "cant annotate tenant-root ns") - return ctrl.Result{}, nil - } - err := annotateTenantRootNs(*hr.Spec.Values, r.Client) - if err != nil { - logger.Error(err, "cant annotate tenant-root ns") - return ctrl.Result{}, nil - } - logger.Info("namespace 'tenant-root' annotated") - } - - if err := r.List(ctx, &hrList, client.InNamespace(childNamespace)); err != nil { - logger.Error(err, "unable to list HelmReleases in namespace", "namespace", hr.Name) - return ctrl.Result{}, err - } - - for _, item := range hrList.Items { - if item.Name == hr.Name { - continue - } - oldDigest := item.GetAnnotations()["cozystack.io/tenant-config-digest"] - if oldDigest == newDigest { - continue - } - patchTarget := item.DeepCopy() - - if patchTarget.Annotations == nil { - patchTarget.Annotations = map[string]string{} - } - ts := time.Now().Format(time.RFC3339Nano) - - patchTarget.Annotations["cozystack.io/tenant-config-digest"] = newDigest - patchTarget.Annotations["reconcile.fluxcd.io/forceAt"] = ts - patchTarget.Annotations["reconcile.fluxcd.io/requestedAt"] = ts - - patch := client.MergeFrom(item.DeepCopy()) - if err := r.Patch(ctx, patchTarget, patch); err != nil { - logger.Error(err, "failed to patch HelmRelease", "name", patchTarget.Name) - continue - } - - logger.Info("patched HelmRelease with new digest", "name", patchTarget.Name, "digest", newDigest, "version", hr.Status.History[0].Version) - } - - return ctrl.Result{}, nil -} - -func (r *TenantHelmReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&helmv2.HelmRelease{}). - Complete(r) -} - -func getChildNamespace(currentNamespace, hrName string) string { - tenantName := strings.TrimPrefix(hrName, "tenant-") - - switch { - case currentNamespace == "tenant-root" && hrName == "tenant-root": - // 1) root tenant inside root namespace - return "tenant-root" - - case currentNamespace == "tenant-root": - // 2) any other tenant in root namespace - return fmt.Sprintf("tenant-%s", tenantName) - - default: - // 3) tenant in a dedicated namespace - return fmt.Sprintf("%s-%s", currentNamespace, tenantName) - } -} - -func annotateTenantRootNs(values apiextensionsv1.JSON, c client.Client) error { - var data map[string]interface{} - if err := yaml.Unmarshal(values.Raw, &data); err != nil { - return fmt.Errorf("failed to parse HelmRelease values: %w", err) - } - - host, ok := data["host"].(string) - if !ok || host == "" { - return fmt.Errorf("host field not found or not a string") - } - - var ns corev1.Namespace - if err := c.Get(context.TODO(), client.ObjectKey{Name: "tenant-root"}, &ns); err != nil { - return fmt.Errorf("failed to get namespace tenant-root: %w", err) - } - - if ns.Annotations == nil { - ns.Annotations = map[string]string{} - } - ns.Annotations["namespace.cozystack.io/host"] = host - - if err := c.Update(context.TODO(), &ns); err != nil { - return fmt.Errorf("failed to update namespace: %w", err) - } - - return nil -} diff --git a/internal/fluxinstall/install.go b/internal/fluxinstall/install.go new file mode 100644 index 00000000..19cb346f --- /dev/null +++ b/internal/fluxinstall/install.go @@ -0,0 +1,352 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fluxinstall + +import ( + "bufio" + "bytes" + "context" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "time" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" + k8syaml "k8s.io/apimachinery/pkg/util/yaml" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" +) + +// Install installs Flux components using embedded manifests. +// It extracts the manifests and applies them to the cluster. +// The namespace is automatically determined from the Namespace object in the manifests. +func Install(ctx context.Context, k8sClient client.Client, writeEmbeddedManifests func(string) error) error { + logger := log.FromContext(ctx) + + // Create temporary directory for manifests + tmpDir, err := os.MkdirTemp("", "flux-install-*") + if err != nil { + return fmt.Errorf("failed to create temp directory: %w", err) + } + defer os.RemoveAll(tmpDir) + + // Extract embedded manifests (generated by cozypkg) + manifestsDir := filepath.Join(tmpDir, "manifests") + if err := os.MkdirAll(manifestsDir, 0755); err != nil { + return fmt.Errorf("failed to create manifests directory: %w", err) + } + + if err := writeEmbeddedManifests(manifestsDir); err != nil { + return fmt.Errorf("failed to extract embedded manifests: %w", err) + } + + // Find the manifest file (should be fluxcd.yaml from cozypkg) + manifestPath := filepath.Join(manifestsDir, "fluxcd.yaml") + if _, err := os.Stat(manifestPath); err != nil { + // Try to find any YAML file if fluxcd.yaml doesn't exist + entries, err := os.ReadDir(manifestsDir) + if err != nil { + return fmt.Errorf("failed to read manifests directory: %w", err) + } + for _, entry := range entries { + if strings.HasSuffix(entry.Name(), ".yaml") { + manifestPath = filepath.Join(manifestsDir, entry.Name()) + break + } + } + } + + // Parse and apply manifests + objects, err := parseManifests(manifestPath) + if err != nil { + return fmt.Errorf("failed to parse manifests: %w", err) + } + + if len(objects) == 0 { + return fmt.Errorf("no objects found in manifests") + } + + // Inject KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT if set in operator environment + if err := injectKubernetesServiceEnv(objects); err != nil { + logger.Info("Failed to inject KUBERNETES_SERVICE_* env vars, continuing anyway", "error", err) + } + + // Extract namespace from Namespace object in manifests + namespace, err := extractNamespace(objects) + if err != nil { + return fmt.Errorf("failed to extract namespace from manifests: %w", err) + } + + logger.Info("Installing Flux components", "namespace", namespace) + + // Apply manifests using server-side apply + logger.Info("Applying Flux manifests", "count", len(objects), "manifest", manifestPath, "namespace", namespace) + if err := applyManifests(ctx, k8sClient, objects); err != nil { + return fmt.Errorf("failed to apply manifests: %w", err) + } + + logger.Info("Flux installation completed successfully") + return nil +} + +// parseManifests parses YAML manifests into unstructured objects. +func parseManifests(manifestPath string) ([]*unstructured.Unstructured, error) { + data, err := os.ReadFile(manifestPath) + if err != nil { + return nil, fmt.Errorf("failed to read manifest file: %w", err) + } + + return readYAMLObjects(bytes.NewReader(data)) +} + +// readYAMLObjects parses multi-document YAML into unstructured objects. +func readYAMLObjects(reader io.Reader) ([]*unstructured.Unstructured, error) { + var objects []*unstructured.Unstructured + yamlReader := k8syaml.NewYAMLReader(bufio.NewReader(reader)) + + for { + doc, err := yamlReader.Read() + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("failed to read YAML document: %w", err) + } + + // Skip empty documents + if len(bytes.TrimSpace(doc)) == 0 { + continue + } + + obj := &unstructured.Unstructured{} + decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(doc), len(doc)) + if err := decoder.Decode(obj); err != nil { + // Skip documents that can't be decoded (might be comments or empty) + if err == io.EOF { + continue + } + return nil, fmt.Errorf("failed to decode YAML document: %w", err) + } + + // Skip empty objects (no kind) + if obj.GetKind() == "" { + continue + } + + objects = append(objects, obj) + } + + return objects, nil +} + +// applyManifests applies Kubernetes objects using server-side apply. +func applyManifests(ctx context.Context, k8sClient client.Client, objects []*unstructured.Unstructured) error { + logger := log.FromContext(ctx) + decoder := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + + // Separate CRDs and namespaces from other resources + var stageOne []*unstructured.Unstructured // CRDs and Namespaces + var stageTwo []*unstructured.Unstructured // Everything else + + for _, obj := range objects { + if isClusterDefinition(obj) { + stageOne = append(stageOne, obj) + } else { + stageTwo = append(stageTwo, obj) + } + } + + // Apply stage one (CRDs and Namespaces) first + if len(stageOne) > 0 { + logger.Info("Applying cluster definitions", "count", len(stageOne)) + if err := applyObjects(ctx, k8sClient, decoder, stageOne); err != nil { + return fmt.Errorf("failed to apply cluster definitions: %w", err) + } + + // Wait a bit for CRDs to be registered + time.Sleep(2 * time.Second) + } + + // Apply stage two (everything else) + if len(stageTwo) > 0 { + logger.Info("Applying resources", "count", len(stageTwo)) + if err := applyObjects(ctx, k8sClient, decoder, stageTwo); err != nil { + return fmt.Errorf("failed to apply resources: %w", err) + } + } + + return nil +} + +// applyObjects applies a list of objects using server-side apply. +func applyObjects(ctx context.Context, k8sClient client.Client, decoder runtime.Decoder, objects []*unstructured.Unstructured) error { + for _, obj := range objects { + // Use server-side apply with force ownership and field manager + // FieldManager is required for apply patch operations + patchOptions := &client.PatchOptions{ + FieldManager: "cozystack-operator", + Force: func() *bool { b := true; return &b }(), + } + + if err := k8sClient.Patch(ctx, obj, client.Apply, patchOptions); err != nil { + return fmt.Errorf("failed to apply object %s/%s: %w", obj.GetKind(), obj.GetName(), err) + } + } + return nil +} + + +// extractNamespace extracts the namespace name from the Namespace object in the manifests. +func extractNamespace(objects []*unstructured.Unstructured) (string, error) { + for _, obj := range objects { + if obj.GetKind() == "Namespace" { + namespace := obj.GetName() + if namespace == "" { + return "", fmt.Errorf("Namespace object has no name") + } + return namespace, nil + } + } + return "", fmt.Errorf("no Namespace object found in manifests") +} + +// isClusterDefinition checks if an object is a CRD or Namespace. +func isClusterDefinition(obj *unstructured.Unstructured) bool { + kind := obj.GetKind() + return kind == "CustomResourceDefinition" || kind == "Namespace" +} + +// injectKubernetesServiceEnv injects KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT +// environment variables into all containers of Deployment, StatefulSet, and DaemonSet objects +// if these variables are set in the operator's environment. +func injectKubernetesServiceEnv(objects []*unstructured.Unstructured) error { + kubernetesHost := os.Getenv("KUBERNETES_SERVICE_HOST") + kubernetesPort := os.Getenv("KUBERNETES_SERVICE_PORT") + + // If neither variable is set, nothing to do + if kubernetesHost == "" && kubernetesPort == "" { + return nil + } + + for _, obj := range objects { + kind := obj.GetKind() + if kind != "Deployment" && kind != "StatefulSet" && kind != "DaemonSet" { + continue + } + + // Navigate to spec.template.spec.containers + spec, found, err := unstructured.NestedMap(obj.Object, "spec", "template", "spec") + if !found || err != nil { + continue + } + + // Update containers + containers, found, err := unstructured.NestedSlice(spec, "containers") + if found && err == nil { + containers = updateContainersEnv(containers, kubernetesHost, kubernetesPort) + if err := unstructured.SetNestedSlice(spec, containers, "containers"); err != nil { + continue + } + } + + // Update initContainers + initContainers, found, err := unstructured.NestedSlice(spec, "initContainers") + if found && err == nil { + initContainers = updateContainersEnv(initContainers, kubernetesHost, kubernetesPort) + if err := unstructured.SetNestedSlice(spec, initContainers, "initContainers"); err != nil { + continue + } + } + + // Update spec in the object + if err := unstructured.SetNestedMap(obj.Object, spec, "spec", "template", "spec"); err != nil { + continue + } + } + + return nil +} + +// updateContainersEnv updates environment variables for a slice of containers. +func updateContainersEnv(containers []interface{}, kubernetesHost, kubernetesPort string) []interface{} { + for i, container := range containers { + containerMap, ok := container.(map[string]interface{}) + if !ok { + continue + } + + env, found, err := unstructured.NestedSlice(containerMap, "env") + if err != nil { + continue + } + + if !found { + env = []interface{}{} + } + + // Update or add KUBERNETES_SERVICE_HOST + if kubernetesHost != "" { + env = setEnvVar(env, "KUBERNETES_SERVICE_HOST", kubernetesHost) + } + + // Update or add KUBERNETES_SERVICE_PORT + if kubernetesPort != "" { + env = setEnvVar(env, "KUBERNETES_SERVICE_PORT", kubernetesPort) + } + + // Update the container's env + if err := unstructured.SetNestedSlice(containerMap, env, "env"); err != nil { + continue + } + + // Update the container in the slice + containers[i] = containerMap + } + + return containers +} + +// setEnvVar updates or adds an environment variable in the env slice. +func setEnvVar(env []interface{}, name, value string) []interface{} { + // Check if variable already exists + for i, envVar := range env { + envVarMap, ok := envVar.(map[string]interface{}) + if !ok { + continue + } + + if envVarMap["name"] == name { + // Update existing variable + envVarMap["value"] = value + env[i] = envVarMap + return env + } + } + + // Add new variable + env = append(env, map[string]interface{}{ + "name": name, + "value": value, + }) + + return env +} + diff --git a/internal/fluxinstall/manifests.embed.go b/internal/fluxinstall/manifests.embed.go new file mode 100644 index 00000000..e6a13a99 --- /dev/null +++ b/internal/fluxinstall/manifests.embed.go @@ -0,0 +1,51 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fluxinstall + +import ( + "embed" + "fmt" + "io/fs" + "os" + "path" +) + +//go:embed manifests/*.yaml +var embeddedFluxManifests embed.FS + +// WriteEmbeddedManifests extracts embedded Flux manifests to a temporary directory. +func WriteEmbeddedManifests(dir string) error { + manifests, err := fs.ReadDir(embeddedFluxManifests, "manifests") + if err != nil { + return fmt.Errorf("failed to read embedded manifests: %w", err) + } + + for _, manifest := range manifests { + data, err := fs.ReadFile(embeddedFluxManifests, path.Join("manifests", manifest.Name())) + if err != nil { + return fmt.Errorf("failed to read file %s: %w", manifest.Name(), err) + } + + outputPath := path.Join(dir, manifest.Name()) + if err := os.WriteFile(outputPath, data, 0666); err != nil { + return fmt.Errorf("failed to write file %s: %w", outputPath, err) + } + } + + return nil +} + diff --git a/internal/fluxinstall/manifests/fluxcd.yaml b/internal/fluxinstall/manifests/fluxcd.yaml new file mode 100644 index 00000000..237db089 --- /dev/null +++ b/internal/fluxinstall/manifests/fluxcd.yaml @@ -0,0 +1,11951 @@ +--- +# Instance: flux +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: alerts.notification.toolkit.fluxcd.io +spec: + group: notification.toolkit.fluxcd.io + names: + kind: Alert + listKind: AlertList + plural: alerts + singular: alert + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 Alert is deprecated, upgrade to v1beta3 + name: v1beta2 + schema: + openAPIV3Schema: + description: Alert is the Schema for the alerts API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: AlertSpec defines an alerting rule for events involving a list of objects. + properties: + eventMetadata: + additionalProperties: + type: string + description: |- + EventMetadata is an optional field for adding metadata to events dispatched by the + controller. This can be used for enhancing the context of the event. If a field + would override one already present on the original event as generated by the emitter, + then the override doesn't happen, i.e. the original value is preserved, and an info + log is printed. + type: object + eventSeverity: + default: info + description: |- + EventSeverity specifies how to filter events based on severity. + If set to 'info' no events will be filtered. + enum: + - info + - error + type: string + eventSources: + description: |- + EventSources specifies how to filter events based + on the involved object kind, name and namespace. + items: + description: |- + CrossNamespaceObjectReference contains enough information to let you locate the + typed referenced object at cluster level + properties: + apiVersion: + description: API version of the referent + type: string + kind: + description: Kind of the referent + enum: + - Bucket + - GitRepository + - Kustomization + - HelmRelease + - HelmChart + - HelmRepository + - ImageRepository + - ImagePolicy + - ImageUpdateAutomation + - OCIRepository + type: string + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + MatchLabels requires the name to be set to `*`. + type: object + name: + description: |- + Name of the referent + If multiple resources are targeted `*` may be set. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent + maxLength: 253 + minLength: 1 + type: string + required: + - kind + - name + type: object + type: array + exclusionList: + description: |- + ExclusionList specifies a list of Golang regular expressions + to be used for excluding messages. + items: + type: string + type: array + inclusionList: + description: |- + InclusionList specifies a list of Golang regular expressions + to be used for including messages. + items: + type: string + type: array + providerRef: + description: ProviderRef specifies which Provider this Alert should use. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + summary: + description: Summary holds a short description of the impact and affected cluster. + maxLength: 255 + type: string + suspend: + description: |- + Suspend tells the controller to suspend subsequent + events handling for this Alert. + type: boolean + required: + - eventSources + - providerRef + type: object + status: + default: + observedGeneration: -1 + description: AlertStatus defines the observed state of the Alert. + properties: + conditions: + description: Conditions holds the conditions for the Alert. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta3 + schema: + openAPIV3Schema: + description: Alert is the Schema for the alerts API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: AlertSpec defines an alerting rule for events involving a list of objects. + properties: + eventMetadata: + additionalProperties: + type: string + description: |- + EventMetadata is an optional field for adding metadata to events dispatched by the + controller. This can be used for enhancing the context of the event. If a field + would override one already present on the original event as generated by the emitter, + then the override doesn't happen, i.e. the original value is preserved, and an info + log is printed. + type: object + eventSeverity: + default: info + description: |- + EventSeverity specifies how to filter events based on severity. + If set to 'info' no events will be filtered. + enum: + - info + - error + type: string + eventSources: + description: |- + EventSources specifies how to filter events based + on the involved object kind, name and namespace. + items: + description: |- + CrossNamespaceObjectReference contains enough information to let you locate the + typed referenced object at cluster level + properties: + apiVersion: + description: API version of the referent + type: string + kind: + description: Kind of the referent + enum: + - Bucket + - GitRepository + - Kustomization + - HelmRelease + - HelmChart + - HelmRepository + - ImageRepository + - ImagePolicy + - ImageUpdateAutomation + - OCIRepository + type: string + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + MatchLabels requires the name to be set to `*`. + type: object + name: + description: |- + Name of the referent + If multiple resources are targeted `*` may be set. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent + maxLength: 253 + minLength: 1 + type: string + required: + - kind + - name + type: object + type: array + exclusionList: + description: |- + ExclusionList specifies a list of Golang regular expressions + to be used for excluding messages. + items: + type: string + type: array + inclusionList: + description: |- + InclusionList specifies a list of Golang regular expressions + to be used for including messages. + items: + type: string + type: array + providerRef: + description: ProviderRef specifies which Provider this Alert should use. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + summary: + description: |- + Summary holds a short description of the impact and affected cluster. + Deprecated: Use EventMetadata instead. + maxLength: 255 + type: string + suspend: + description: |- + Suspend tells the controller to suspend subsequent + events handling for this Alert. + type: boolean + required: + - eventSources + - providerRef + type: object + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: artifactgenerators.source.extensions.fluxcd.io +spec: + group: source.extensions.fluxcd.io + names: + kind: ArtifactGenerator + listKind: ArtifactGeneratorList + plural: artifactgenerators + shortNames: + - ag + singular: artifactgenerator + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: ArtifactGenerator is the Schema for the artifactgenerators API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ArtifactGeneratorSpec defines the desired state of ArtifactGenerator. + properties: + artifacts: + description: OutputArtifacts is a list of output artifacts to be generated. + items: + description: |- + OutputArtifact defines the desired state of an ExternalArtifact + generated by the ArtifactGenerator. + properties: + copy: + description: |- + Copy defines a list of copy operations to perform from the sources to the generated artifact. + The copy operations are performed in the order they are listed with existing files + being overwritten by later copy operations. + items: + properties: + exclude: + description: |- + Exclude specifies a list of glob patterns to exclude + files and dirs matched by the 'From' field. + items: + type: string + maxItems: 100 + type: array + from: + description: |- + From specifies the source (by alias) and the glob pattern to match files. + The format is "@/". + maxLength: 1024 + pattern: ^@([a-z0-9]([a-z0-9_-]*[a-z0-9])?)/(.*)$ + type: string + strategy: + description: |- + Strategy specifies the copy strategy to use. + 'Overwrite' will overwrite existing files in the destination. + 'Merge' is for merging YAML files using Helm values merge strategy. + If not specified, defaults to 'Overwrite'. + enum: + - Overwrite + - Merge + type: string + to: + description: |- + To specifies the destination path within the artifact. + The format is "@artifact/path", the alias "artifact" + refers to the root path of the generated artifact. + maxLength: 1024 + pattern: ^@(artifact)/(.*)$ + type: string + required: + - from + - to + type: object + minItems: 1 + type: array + name: + description: Name is the name of the generated artifact. + maxLength: 253 + pattern: ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ + type: string + originRevision: + description: |- + OriginRevision is used to set the 'org.opencontainers.image.revision' + annotation on the generated artifact metadata. + If specified, it must point to an existing source alias in the format "@". + If the referenced source has an origin revision (e.g. a Git commit SHA), + it will be used to set the annotation on the generated artifact. + If the referenced source does not have an origin revision, the field is ignored. + maxLength: 64 + pattern: ^@([a-z0-9]([a-z0-9_-]*[a-z0-9])?)$ + type: string + revision: + description: |- + Revision is the revision of the generated artifact. + If specified, it must point to an existing source alias in the format "@". + If not specified, the revision is automatically set to the digest of the artifact content. + maxLength: 64 + pattern: ^@([a-z0-9]([a-z0-9_-]*[a-z0-9])?)$ + type: string + required: + - copy + - name + type: object + maxItems: 1000 + minItems: 1 + type: array + sources: + description: |- + Sources is a list of references to the Flux source-controller + resources that will be used to generate the artifact. + items: + description: SourceReference contains the reference to a Flux source-controller resource. + properties: + alias: + description: |- + Alias of the source within the ArtifactGenerator context. + The alias must be unique per ArtifactGenerator, and must consist + of lower case alphanumeric characters, underscores, and hyphens. + It must start and end with an alphanumeric character. + maxLength: 63 + pattern: ^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$ + type: string + kind: + description: Kind of the source. + enum: + - Bucket + - GitRepository + - OCIRepository + type: string + name: + description: Name of the source. + maxLength: 253 + pattern: ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ + type: string + namespace: + description: |- + Namespace of the source. + If not provided, defaults to the same namespace as the ArtifactGenerator. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - alias + - kind + - name + type: object + maxItems: 1000 + minItems: 1 + type: array + required: + - artifacts + - sources + type: object + status: + description: ArtifactGeneratorStatus defines the observed state of ArtifactGenerator. + properties: + conditions: + description: Conditions holds the conditions for the ArtifactGenerator. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + inventory: + description: Inventory contains the list of generated ExternalArtifact references. + items: + description: |- + ExternalArtifactReference contains the reference to a + generated ExternalArtifact along with its digest. + properties: + digest: + description: Digest of the referent artifact. + type: string + filename: + description: Filename is the name of the artifact file. + type: string + name: + description: Name of the referent artifact. + type: string + namespace: + description: Namespace of the referent artifact. + type: string + required: + - digest + - filename + - name + - namespace + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedSourcesDigest: + description: |- + ObservedSourcesDigest is a hash representing the current state of + all the sources referenced by the ArtifactGenerator. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: buckets.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: Bucket + listKind: BucketList + plural: buckets + singular: bucket + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.endpoint + name: Endpoint + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: Bucket is the Schema for the buckets API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + BucketSpec specifies the required configuration to produce an Artifact for + an object storage bucket. + properties: + bucketName: + description: BucketName is the name of the object storage bucket. + type: string + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + bucket. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + This field is only supported for the `generic` provider. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + endpoint: + description: Endpoint is the object storage address the BucketName is located at. + type: string + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP Endpoint. + type: boolean + interval: + description: |- + Interval at which the Bucket Endpoint is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + prefix: + description: Prefix to use for server-side filtering of files in the Bucket. + type: string + provider: + default: generic + description: |- + Provider of the object storage bucket. + Defaults to 'generic', which expects an S3 (API) compatible object + storage. + enum: + - generic + - aws + - gcp + - azure + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the Bucket server. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + region: + description: Region of the Endpoint where the BucketName is located in. + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the Bucket. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the bucket. This field is only supported for the 'gcp' and 'aws' providers. + For more information about workload identity: + https://fluxcd.io/flux/components/source/buckets/#workload-identity + type: string + sts: + description: |- + STS specifies the required configuration to use a Security Token + Service for fetching temporary credentials to authenticate in a + Bucket provider. + + This field is only supported for the `aws` and `generic` providers. + properties: + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + STS endpoint. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + This field is only supported for the `ldap` provider. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + endpoint: + description: |- + Endpoint is the HTTP/S endpoint of the Security Token Service from + where temporary credentials will be fetched. + pattern: ^(http|https)://.*$ + type: string + provider: + description: Provider of the Security Token Service. + enum: + - aws + - ldap + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the STS endpoint. This Secret must contain the fields `username` + and `password` and is supported only for the `ldap` provider. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - endpoint + - provider + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + Bucket. + type: boolean + timeout: + default: 60s + description: Timeout for fetch operations, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + required: + - bucketName + - endpoint + - interval + type: object + x-kubernetes-validations: + - message: STS configuration is only supported for the 'aws' and 'generic' Bucket providers + rule: self.provider == 'aws' || self.provider == 'generic' || !has(self.sts) + - message: '''aws'' is the only supported STS provider for the ''aws'' Bucket provider' + rule: self.provider != 'aws' || !has(self.sts) || self.sts.provider == 'aws' + - message: '''ldap'' is the only supported STS provider for the ''generic'' Bucket provider' + rule: self.provider != 'generic' || !has(self.sts) || self.sts.provider == 'ldap' + - message: spec.sts.secretRef is not required for the 'aws' STS provider + rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.secretRef)' + - message: spec.sts.certSecretRef is not required for the 'aws' STS provider + rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.certSecretRef)' + - message: ServiceAccountName is not supported for the 'generic' Bucket provider + rule: self.provider != 'generic' || !has(self.serviceAccountName) + - message: cannot set both .spec.secretRef and .spec.serviceAccountName + rule: '!has(self.secretRef) || !has(self.serviceAccountName)' + status: + default: + observedGeneration: -1 + description: BucketStatus records the observed state of a Bucket. + properties: + artifact: + description: Artifact represents the last successful Bucket reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the Bucket. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of the Bucket object. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + BucketStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.endpoint + name: Endpoint + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 Bucket is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: Bucket is the Schema for the buckets API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + BucketSpec specifies the required configuration to produce an Artifact for + an object storage bucket. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + bucketName: + description: BucketName is the name of the object storage bucket. + type: string + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + bucket. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + This field is only supported for the `generic` provider. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + endpoint: + description: Endpoint is the object storage address the BucketName is located at. + type: string + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP Endpoint. + type: boolean + interval: + description: |- + Interval at which the Bucket Endpoint is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + prefix: + description: Prefix to use for server-side filtering of files in the Bucket. + type: string + provider: + default: generic + description: |- + Provider of the object storage bucket. + Defaults to 'generic', which expects an S3 (API) compatible object + storage. + enum: + - generic + - aws + - gcp + - azure + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the Bucket server. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + region: + description: Region of the Endpoint where the BucketName is located in. + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the Bucket. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + sts: + description: |- + STS specifies the required configuration to use a Security Token + Service for fetching temporary credentials to authenticate in a + Bucket provider. + + This field is only supported for the `aws` and `generic` providers. + properties: + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + STS endpoint. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + This field is only supported for the `ldap` provider. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + endpoint: + description: |- + Endpoint is the HTTP/S endpoint of the Security Token Service from + where temporary credentials will be fetched. + pattern: ^(http|https)://.*$ + type: string + provider: + description: Provider of the Security Token Service. + enum: + - aws + - ldap + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the STS endpoint. This Secret must contain the fields `username` + and `password` and is supported only for the `ldap` provider. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - endpoint + - provider + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + Bucket. + type: boolean + timeout: + default: 60s + description: Timeout for fetch operations, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + required: + - bucketName + - endpoint + - interval + type: object + x-kubernetes-validations: + - message: STS configuration is only supported for the 'aws' and 'generic' Bucket providers + rule: self.provider == 'aws' || self.provider == 'generic' || !has(self.sts) + - message: '''aws'' is the only supported STS provider for the ''aws'' Bucket provider' + rule: self.provider != 'aws' || !has(self.sts) || self.sts.provider == 'aws' + - message: '''ldap'' is the only supported STS provider for the ''generic'' Bucket provider' + rule: self.provider != 'generic' || !has(self.sts) || self.sts.provider == 'ldap' + - message: spec.sts.secretRef is not required for the 'aws' STS provider + rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.secretRef)' + - message: spec.sts.certSecretRef is not required for the 'aws' STS provider + rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.certSecretRef)' + status: + default: + observedGeneration: -1 + description: BucketStatus records the observed state of a Bucket. + properties: + artifact: + description: Artifact represents the last successful Bucket reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the Bucket. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of the Bucket object. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + BucketStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: externalartifacts.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: ExternalArtifact + listKind: ExternalArtifactList + plural: externalartifacts + singular: externalartifact + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .spec.sourceRef.name + name: Source + type: string + name: v1 + schema: + openAPIV3Schema: + description: ExternalArtifact is the Schema for the external artifacts API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ExternalArtifactSpec defines the desired state of ExternalArtifact + properties: + sourceRef: + description: |- + SourceRef points to the Kubernetes custom resource for + which the artifact is generated. + properties: + apiVersion: + description: API version of the referent, if not specified the Kubernetes preferred version will be used. + type: string + kind: + description: Kind of the referent. + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it acts as LocalObjectReference. + type: string + required: + - kind + - name + type: object + type: object + status: + description: ExternalArtifactStatus defines the observed state of ExternalArtifact + properties: + artifact: + description: Artifact represents the output of an ExternalArtifact reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the ExternalArtifact. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: gitrepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: GitRepository + listKind: GitRepositoryList + plural: gitrepositories + shortNames: + - gitrepo + singular: gitrepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: GitRepository is the Schema for the gitrepositories API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + GitRepositorySpec specifies the required configuration to produce an + Artifact for a Git repository. + properties: + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + include: + description: |- + Include specifies a list of GitRepository resources which Artifacts + should be included in the Artifact produced for this GitRepository. + items: + description: |- + GitRepositoryInclude specifies a local reference to a GitRepository which + Artifact (sub-)contents must be included, and where they should be placed. + properties: + fromPath: + description: |- + FromPath specifies the path to copy contents from, defaults to the root + of the Artifact. + type: string + repository: + description: |- + GitRepositoryRef specifies the GitRepository which Artifact contents + must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: |- + ToPath specifies the path to copy contents to, defaults to the name of + the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + interval: + description: |- + Interval at which the GitRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + provider: + description: |- + Provider used for authentication, can be 'azure', 'github', 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - azure + - github + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the Git server. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + recurseSubmodules: + description: |- + RecurseSubmodules enables the initialization of all submodules within + the GitRepository as cloned from the URL, using their default settings. + type: boolean + ref: + description: |- + Reference specifies the Git reference to resolve and monitor for + changes, defaults to the 'master' branch. + properties: + branch: + description: Branch to check out, defaults to 'master' if no other field is defined. + type: string + commit: + description: |- + Commit SHA to check out, takes precedence over all reference fields. + + This can be combined with Branch to shallow clone the branch, in which + the commit is expected to exist. + type: string + name: + description: |- + Name of the reference to check out; takes precedence over Branch, Tag and SemVer. + + It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description + Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" + type: string + semver: + description: SemVer tag expression to check out, takes precedence over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials for + the GitRepository. + For HTTPS repositories the Secret must contain 'username' and 'password' + fields for basic auth or 'bearerToken' field for token auth. + For SSH repositories the Secret must contain 'identity' + and 'known_hosts' fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to + authenticate to the GitRepository. This field is only supported for 'azure' provider. + type: string + sparseCheckout: + description: |- + SparseCheckout specifies a list of directories to checkout when cloning + the repository. If specified, only these directories are included in the + Artifact produced for this GitRepository. + items: + type: string + type: array + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + GitRepository. + type: boolean + timeout: + default: 60s + description: Timeout for Git operations like cloning, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + url: + description: URL specifies the Git repository URL, it can be an HTTP/S or SSH address. + pattern: ^(http|https|ssh)://.*$ + type: string + verify: + description: |- + Verification specifies the configuration to verify the Git commit + signature(s). + properties: + mode: + default: HEAD + description: |- + Mode specifies which Git object(s) should be verified. + + The variants "head" and "HEAD" both imply the same thing, i.e. verify + the commit that the HEAD of the Git repository points to. The variant + "head" solely exists to ensure backwards compatibility. + enum: + - head + - HEAD + - Tag + - TagAndHEAD + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing the public keys of trusted Git + authors. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - secretRef + type: object + required: + - interval + - url + type: object + x-kubernetes-validations: + - message: serviceAccountName can only be set when provider is 'azure' + rule: '!has(self.serviceAccountName) || (has(self.provider) && self.provider == ''azure'')' + status: + default: + observedGeneration: -1 + description: GitRepositoryStatus records the observed state of a Git repository. + properties: + artifact: + description: Artifact represents the last successful GitRepository reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the GitRepository. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + includedArtifacts: + description: |- + IncludedArtifacts contains a list of the last successfully included + Artifacts as instructed by GitRepositorySpec.Include. + items: + description: Artifact represents the output of a Source reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the GitRepository + object. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + observedInclude: + description: |- + ObservedInclude is the observed list of GitRepository resources used to + produce the current Artifact. + items: + description: |- + GitRepositoryInclude specifies a local reference to a GitRepository which + Artifact (sub-)contents must be included, and where they should be placed. + properties: + fromPath: + description: |- + FromPath specifies the path to copy contents from, defaults to the root + of the Artifact. + type: string + repository: + description: |- + GitRepositoryRef specifies the GitRepository which Artifact contents + must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: |- + ToPath specifies the path to copy contents to, defaults to the name of + the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + observedRecurseSubmodules: + description: |- + ObservedRecurseSubmodules is the observed resource submodules + configuration used to produce the current Artifact. + type: boolean + observedSparseCheckout: + description: |- + ObservedSparseCheckout is the observed list of directories used to + produce the current Artifact. + items: + type: string + type: array + sourceVerificationMode: + description: |- + SourceVerificationMode is the last used verification mode indicating + which Git object(s) have been verified. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 GitRepository is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: GitRepository is the Schema for the gitrepositories API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + GitRepositorySpec specifies the required configuration to produce an + Artifact for a Git repository. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + gitImplementation: + default: go-git + description: |- + GitImplementation specifies which Git client library implementation to + use. Defaults to 'go-git', valid values are ('go-git', 'libgit2'). + Deprecated: gitImplementation is deprecated now that 'go-git' is the + only supported implementation. + enum: + - go-git + - libgit2 + type: string + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + include: + description: |- + Include specifies a list of GitRepository resources which Artifacts + should be included in the Artifact produced for this GitRepository. + items: + description: |- + GitRepositoryInclude specifies a local reference to a GitRepository which + Artifact (sub-)contents must be included, and where they should be placed. + properties: + fromPath: + description: |- + FromPath specifies the path to copy contents from, defaults to the root + of the Artifact. + type: string + repository: + description: |- + GitRepositoryRef specifies the GitRepository which Artifact contents + must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: |- + ToPath specifies the path to copy contents to, defaults to the name of + the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + interval: + description: Interval at which to check the GitRepository for updates. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + recurseSubmodules: + description: |- + RecurseSubmodules enables the initialization of all submodules within + the GitRepository as cloned from the URL, using their default settings. + type: boolean + ref: + description: |- + Reference specifies the Git reference to resolve and monitor for + changes, defaults to the 'master' branch. + properties: + branch: + description: Branch to check out, defaults to 'master' if no other field is defined. + type: string + commit: + description: |- + Commit SHA to check out, takes precedence over all reference fields. + + This can be combined with Branch to shallow clone the branch, in which + the commit is expected to exist. + type: string + name: + description: |- + Name of the reference to check out; takes precedence over Branch, Tag and SemVer. + + It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description + Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" + type: string + semver: + description: SemVer tag expression to check out, takes precedence over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials for + the GitRepository. + For HTTPS repositories the Secret must contain 'username' and 'password' + fields for basic auth or 'bearerToken' field for token auth. + For SSH repositories the Secret must contain 'identity' + and 'known_hosts' fields. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + GitRepository. + type: boolean + timeout: + default: 60s + description: Timeout for Git operations like cloning, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + url: + description: URL specifies the Git repository URL, it can be an HTTP/S or SSH address. + pattern: ^(http|https|ssh)://.*$ + type: string + verify: + description: |- + Verification specifies the configuration to verify the Git commit + signature(s). + properties: + mode: + description: Mode specifies what Git object should be verified, currently ('head'). + enum: + - head + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing the public keys of trusted Git + authors. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - mode + - secretRef + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: GitRepositoryStatus records the observed state of a Git repository. + properties: + artifact: + description: Artifact represents the last successful GitRepository reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the GitRepository. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + contentConfigChecksum: + description: |- + ContentConfigChecksum is a checksum of all the configurations related to + the content of the source artifact: + - .spec.ignore + - .spec.recurseSubmodules + - .spec.included and the checksum of the included artifacts + observed in .status.observedGeneration version of the object. This can + be used to determine if the content of the included repository has + changed. + It has the format of `:`, for example: `sha256:`. + + Deprecated: Replaced with explicit fields for observed artifact content + config in the status. + type: string + includedArtifacts: + description: |- + IncludedArtifacts contains a list of the last successfully included + Artifacts as instructed by GitRepositorySpec.Include. + items: + description: Artifact represents the output of a Source reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the GitRepository + object. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + observedInclude: + description: |- + ObservedInclude is the observed list of GitRepository resources used to + to produce the current Artifact. + items: + description: |- + GitRepositoryInclude specifies a local reference to a GitRepository which + Artifact (sub-)contents must be included, and where they should be placed. + properties: + fromPath: + description: |- + FromPath specifies the path to copy contents from, defaults to the root + of the Artifact. + type: string + repository: + description: |- + GitRepositoryRef specifies the GitRepository which Artifact contents + must be included. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + toPath: + description: |- + ToPath specifies the path to copy contents to, defaults to the name of + the GitRepositoryRef. + type: string + required: + - repository + type: object + type: array + observedRecurseSubmodules: + description: |- + ObservedRecurseSubmodules is the observed resource submodules + configuration used to produce the current Artifact. + type: boolean + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + GitRepositoryStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: helmcharts.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: HelmChart + listKind: HelmChartList + plural: helmcharts + shortNames: + - hc + singular: helmchart + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.chart + name: Chart + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.sourceRef.kind + name: Source Kind + type: string + - jsonPath: .spec.sourceRef.name + name: Source Name + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: HelmChart is the Schema for the helmcharts API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmChartSpec specifies the desired state of a Helm chart. + properties: + chart: + description: |- + Chart is the name or path the Helm chart is available at in the + SourceRef. + type: string + ignoreMissingValuesFiles: + description: |- + IgnoreMissingValuesFiles controls whether to silently ignore missing values + files rather than failing. + type: boolean + interval: + description: |- + Interval at which the HelmChart SourceRef is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + ReconcileStrategy determines what enables the creation of a new artifact. + Valid values are ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: SourceRef is the reference to the Source the chart is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: |- + Kind of the referent, valid values are ('HelmRepository', 'GitRepository', + 'Bucket'). + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + source. + type: boolean + valuesFiles: + description: |- + ValuesFiles is an alternative list of values files to use as the chart + values (values.yaml is not included by default), expected to be a + relative path in the SourceRef. + Values files are merged in the order of this list with the last file + overriding the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported when using HelmRepository source with spec.type 'oci'. + Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. + properties: + matchOIDCIdentity: + description: |- + MatchOIDCIdentity specifies the identity matching criteria to use + while verifying an OCI artifact which was signed using Cosign keyless + signing. The artifact's identity is deemed to be verified if any of the + specified matchers match against the identity. + items: + description: |- + OIDCIdentityMatch specifies options for verifying the certificate identity, + i.e. the issuer and the subject of the certificate. + properties: + issuer: + description: |- + Issuer specifies the regex pattern to match against to verify + the OIDC issuer in the Fulcio certificate. The pattern must be a + valid Go regular expression. + type: string + subject: + description: |- + Subject specifies the regex pattern to match against to verify + the identity subject in the Fulcio certificate. The pattern must + be a valid Go regular expression. + type: string + required: + - issuer + - subject + type: object + type: array + provider: + default: cosign + description: Provider specifies the technology used to sign the OCI Artifact. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version is the chart version semver expression, ignored for charts from + GitRepository and Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: HelmChartStatus records the observed state of the HelmChart. + properties: + artifact: + description: Artifact represents the output of the last successful reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmChart. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedChartName: + description: |- + ObservedChartName is the last observed chart name as specified by the + resolved chart reference. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the HelmChart + object. + format: int64 + type: integer + observedSourceArtifactRevision: + description: |- + ObservedSourceArtifactRevision is the last observed Artifact.Revision + of the HelmChartSpec.SourceRef. + type: string + observedValuesFiles: + description: |- + ObservedValuesFiles are the observed value files of the last successful + reconciliation. + It matches the chart in the last successfully reconciled artifact. + items: + type: string + type: array + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + BucketStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.chart + name: Chart + type: string + - jsonPath: .spec.version + name: Version + type: string + - jsonPath: .spec.sourceRef.kind + name: Source Kind + type: string + - jsonPath: .spec.sourceRef.name + name: Source Name + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 HelmChart is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: HelmChart is the Schema for the helmcharts API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmChartSpec specifies the desired state of a Helm chart. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + chart: + description: |- + Chart is the name or path the Helm chart is available at in the + SourceRef. + type: string + ignoreMissingValuesFiles: + description: |- + IgnoreMissingValuesFiles controls whether to silently ignore missing values + files rather than failing. + type: boolean + interval: + description: |- + Interval at which the HelmChart SourceRef is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + ReconcileStrategy determines what enables the creation of a new artifact. + Valid values are ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: SourceRef is the reference to the Source the chart is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: |- + Kind of the referent, valid values are ('HelmRepository', 'GitRepository', + 'Bucket'). + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + source. + type: boolean + valuesFile: + description: |- + ValuesFile is an alternative values file to use as the default chart + values, expected to be a relative path in the SourceRef. Deprecated in + favor of ValuesFiles, for backwards compatibility the file specified here + is merged before the ValuesFiles items. Ignored when omitted. + type: string + valuesFiles: + description: |- + ValuesFiles is an alternative list of values files to use as the chart + values (values.yaml is not included by default), expected to be a + relative path in the SourceRef. + Values files are merged in the order of this list with the last file + overriding the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported when using HelmRepository source with spec.type 'oci'. + Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. + properties: + matchOIDCIdentity: + description: |- + MatchOIDCIdentity specifies the identity matching criteria to use + while verifying an OCI artifact which was signed using Cosign keyless + signing. The artifact's identity is deemed to be verified if any of the + specified matchers match against the identity. + items: + description: |- + OIDCIdentityMatch specifies options for verifying the certificate identity, + i.e. the issuer and the subject of the certificate. + properties: + issuer: + description: |- + Issuer specifies the regex pattern to match against to verify + the OIDC issuer in the Fulcio certificate. The pattern must be a + valid Go regular expression. + type: string + subject: + description: |- + Subject specifies the regex pattern to match against to verify + the identity subject in the Fulcio certificate. The pattern must + be a valid Go regular expression. + type: string + required: + - issuer + - subject + type: object + type: array + provider: + default: cosign + description: Provider specifies the technology used to sign the OCI Artifact. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version is the chart version semver expression, ignored for charts from + GitRepository and Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: HelmChartStatus records the observed state of the HelmChart. + properties: + artifact: + description: Artifact represents the output of the last successful reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmChart. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedChartName: + description: |- + ObservedChartName is the last observed chart name as specified by the + resolved chart reference. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the HelmChart + object. + format: int64 + type: integer + observedSourceArtifactRevision: + description: |- + ObservedSourceArtifactRevision is the last observed Artifact.Revision + of the HelmChartSpec.SourceRef. + type: string + observedValuesFiles: + description: |- + ObservedValuesFiles are the observed value files of the last successful + reconciliation. + It matches the chart in the last successfully reconciled artifact. + items: + type: string + type: array + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + BucketStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: helmreleases.helm.toolkit.fluxcd.io +spec: + group: helm.toolkit.fluxcd.io + names: + kind: HelmRelease + listKind: HelmReleaseList + plural: helmreleases + shortNames: + - hr + singular: helmrelease + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v2 + schema: + openAPIV3Schema: + description: HelmRelease is the Schema for the helmreleases API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmReleaseSpec defines the desired state of a Helm release. + properties: + chart: + description: |- + Chart defines the template of the v1.HelmChart that should be created + for this HelmRelease. + properties: + metadata: + description: ObjectMeta holds the template for metadata like labels and annotations. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + type: object + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + type: object + type: object + spec: + description: Spec holds the template for the v1.HelmChartSpec for this HelmRelease. + properties: + chart: + description: The name or path the Helm chart is available at in the SourceRef. + maxLength: 2048 + minLength: 1 + type: string + ignoreMissingValuesFiles: + description: IgnoreMissingValuesFiles controls whether to silently ignore missing values files rather than failing. + type: boolean + interval: + description: |- + Interval at which to check the v1.Source for updates. Defaults to + 'HelmReleaseSpec.Interval'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + Determines what enables the creation of a new artifact. Valid values are + ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: The name and namespace of the v1.Source the chart is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent. + maxLength: 63 + minLength: 1 + type: string + required: + - kind + - name + type: object + valuesFiles: + description: |- + Alternative list of values files to use as the chart values (values.yaml + is not included by default), expected to be a relative path in the SourceRef. + Values files are merged in the order of this list with the last file overriding + the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported for OCI sources. + Chart dependencies, which are not bundled in the umbrella chart artifact, + are not verified. + properties: + provider: + default: cosign + description: Provider specifies the technology used to sign the OCI Helm chart. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version semver expression, ignored for charts from v1.GitRepository and + v1beta2.Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - sourceRef + type: object + required: + - spec + type: object + chartRef: + description: |- + ChartRef holds a reference to a source controller resource containing the + Helm chart artifact. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - HelmChart + - ExternalArtifact + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kubernetes + resource object that contains the reference. + maxLength: 63 + minLength: 1 + type: string + required: + - kind + - name + type: object + commonMetadata: + description: |- + CommonMetadata specifies the common labels and annotations that are + applied to all resources. Any existing label or annotation will be + overridden if its key matches a common one. + properties: + annotations: + additionalProperties: + type: string + description: Annotations to be added to the object's metadata. + type: object + labels: + additionalProperties: + type: string + description: Labels to be added to the object's metadata. + type: object + type: object + dependsOn: + description: |- + DependsOn may contain a DependencyReference slice with + references to HelmRelease resources that must be ready before this HelmRelease + can be reconciled. + items: + description: DependencyReference defines a HelmRelease dependency on another HelmRelease resource. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the HelmRelease + resource object that contains the reference. + type: string + readyExpr: + description: |- + ReadyExpr is a CEL expression that can be used to assess the readiness + of a dependency. When specified, the built-in readiness check + is replaced by the logic defined in the CEL expression. + To make the CEL expression additive to the built-in readiness check, + the feature gate `AdditiveCELDependencyCheck` must be set to `true`. + type: string + required: + - name + type: object + type: array + driftDetection: + description: |- + DriftDetection holds the configuration for detecting and handling + differences between the manifest in the Helm storage and the resources + currently existing in the cluster. + properties: + ignore: + description: |- + Ignore contains a list of rules for specifying which changes to ignore + during diffing. + items: + description: |- + IgnoreRule defines a rule to selectively disregard specific changes during + the drift detection process. + properties: + paths: + description: |- + Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from + consideration in a Kubernetes object. + items: + type: string + type: array + target: + description: |- + Target is a selector for specifying Kubernetes objects to which this + rule applies. + If Target is not set, the Paths will be ignored for all Kubernetes + objects within the manifest of the Helm release. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - paths + type: object + type: array + mode: + description: |- + Mode defines how differences should be handled between the Helm manifest + and the manifest currently applied to the cluster. + If not explicitly set, it defaults to DiffModeDisabled. + enum: + - enabled + - warn + - disabled + type: string + type: object + install: + description: Install holds the configuration for Helm install actions for this HelmRelease. + properties: + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Create` and if omitted + CRDs are installed but not updated. + + Skip: do neither install nor replace (update) any CRDs. + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + By default, CRDs are applied (installed) during Helm install action. + With this option users can opt in to CRD replace existing CRDs on Helm + install actions, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + createNamespace: + description: |- + CreateNamespace tells the Helm install action to create the + HelmReleaseSpec.TargetNamespace if it does not exist yet. + On uninstall, the namespace will not be garbage collected. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the Helm install action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm install action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableSchemaValidation: + description: |- + DisableSchemaValidation prevents the Helm install action from validating + the values against the JSON Schema. + type: boolean + disableTakeOwnership: + description: |- + DisableTakeOwnership disables taking ownership of existing resources + during the Helm install action. Defaults to false. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + install has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + install has been performed. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm install + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an install action but fail. Defaults to + 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false'. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using an uninstall, is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + type: object + replace: + description: |- + Replace tells the Helm install action to re-use the 'ReleaseName', but only + if that name is a deleted release which remains in the history. + type: boolean + skipCRDs: + description: |- + SkipCRDs tells the Helm install action to not install any CRDs. By default, + CRDs are installed if not already present. + + Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. + type: boolean + strategy: + description: |- + Strategy defines the install strategy to use for this HelmRelease. + Defaults to 'RemediateOnFailure'. + properties: + name: + description: Name of the install strategy. + enum: + - RemediateOnFailure + - RetryOnFailure + type: string + retryInterval: + description: |- + RetryInterval is the interval at which to retry a failed install. + Can be used only when Name is set to RetryOnFailure. + Defaults to '5m'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + required: + - name + type: object + x-kubernetes-validations: + - message: .retryInterval cannot be set when .name is 'RemediateOnFailure' + rule: '!has(self.retryInterval) || self.name != ''RemediateOnFailure''' + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm install action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + interval: + description: Interval at which to reconcile the Helm release. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: |- + KubeConfig for reconciling the HelmRelease on a remote cluster. + When used in combination with HelmReleaseSpec.ServiceAccountName, + forces the controller to act on behalf of that Service Account at the + target cluster. + If the --default-service-account flag is set, its value will be used as + a controller level fallback for when HelmReleaseSpec.ServiceAccountName + is empty. + properties: + configMapRef: + description: |- + ConfigMapRef holds an optional name of a ConfigMap that contains + the following keys: + + - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or + `generic`. Required. + - `cluster`: the fully qualified resource name of the Kubernetes + cluster in the cloud provider API. Not used by the `generic` + provider. Required when one of `address` or `ca.crt` is not set. + - `address`: the address of the Kubernetes API server. Required + for `generic`. For the other providers, if not specified, the + first address in the cluster resource will be used, and if + specified, it must match one of the addresses in the cluster + resource. + If audiences is not set, will be used as the audience for the + `generic` provider. + - `ca.crt`: the optional PEM-encoded CA certificate for the + Kubernetes API server. If not set, the controller will use the + CA certificate from the cluster resource. + - `audiences`: the optional audiences as a list of + line-break-separated strings for the Kubernetes ServiceAccount + token. Defaults to the `address` for the `generic` provider, or + to specific values for the other providers depending on the + provider. + - `serviceAccountName`: the optional name of the Kubernetes + ServiceAccount in the same namespace that should be used + for authentication. If not specified, the controller + ServiceAccount will be used. + + Mutually exclusive with SecretRef. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef holds an optional name of a secret that contains a key with + the kubeconfig file as the value. If no key is set, the key will default + to 'value'. Mutually exclusive with ConfigMapRef. + It is recommended that the kubeconfig is self-contained, and the secret + is regularly updated if credentials such as a cloud-access-token expire. + Cloud specific `cmd-path` auth helpers will not function without adding + binaries and credentials to the Pod that is responsible for reconciling + Kubernetes resources. Supported only for the generic provider. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + x-kubernetes-validations: + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified + rule: has(self.configMapRef) || has(self.secretRef) + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified + rule: '!has(self.configMapRef) || !has(self.secretRef)' + maxHistory: + description: |- + MaxHistory is the number of revisions saved by Helm for this HelmRelease. + Use '0' for an unlimited number of revisions; defaults to '5'. + type: integer + persistentClient: + description: |- + PersistentClient tells the controller to use a persistent Kubernetes + client for this release. When enabled, the client will be reused for the + duration of the reconciliation, instead of being created and destroyed + for each (step of a) Helm action. + + This can improve performance, but may cause issues with some Helm charts + that for example do create Custom Resource Definitions during installation + outside Helm's CRD lifecycle hooks, which are then not observed to be + available by e.g. post-install hooks. + + If not set, it defaults to true. + type: boolean + postRenderers: + description: |- + PostRenderers holds an array of Helm PostRenderers, which will be applied in order + of their definition. + items: + description: PostRenderer contains a Helm PostRenderer specification. + properties: + kustomize: + description: Kustomization to apply as PostRenderer. + properties: + images: + description: |- + Images is a list of (image name, new name, new tag or digest) + for changing image names, tags or digests. This can also be achieved with a + patch, but this operator is simpler to specify. + items: + description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag. + properties: + digest: + description: |- + Digest is the value used to replace the original image tag. + If digest is present NewTag value is ignored. + type: string + name: + description: Name is a tag-less image name. + type: string + newName: + description: NewName is the value used to replace the original name. + type: string + newTag: + description: NewTag is the value used to replace the original tag. + type: string + required: + - name + type: object + type: array + patches: + description: |- + Strategic merge and JSON patches, defined as inline YAML objects, + capable of targeting objects based on kind, label and annotation selectors. + items: + description: |- + Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should + be applied to. + properties: + patch: + description: |- + Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with + an array of operation objects. + type: string + target: + description: Target points to the resources that the patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + type: object + type: array + type: object + type: object + type: array + releaseName: + description: |- + ReleaseName used for the Helm release. Defaults to a composition of + '[TargetNamespace-]Name'. + maxLength: 53 + minLength: 1 + type: string + rollback: + description: Rollback holds the configuration for Helm rollback actions for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + rollback action when it fails. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + rollback has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + rollback has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement strategy. + type: boolean + recreate: + description: Recreate performs pod restarts for the resource if applicable. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm rollback action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + serviceAccountName: + description: |- + The name of the Kubernetes service account to impersonate + when reconciling this HelmRelease. + maxLength: 253 + minLength: 1 + type: string + storageNamespace: + description: |- + StorageNamespace used for the Helm storage. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + suspend: + description: |- + Suspend tells the controller to suspend reconciliation for this HelmRelease, + it does not apply to already started reconciliations. Defaults to false. + type: boolean + targetNamespace: + description: |- + TargetNamespace to target when performing operations for the HelmRelease. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + test: + description: Test holds the configuration for Helm test actions for this HelmRelease. + properties: + enable: + description: |- + Enable enables Helm test actions for this HelmRelease after an Helm install + or upgrade action has been performed. + type: boolean + filters: + description: Filters is a list of tests to run or exclude from running. + items: + description: Filter holds the configuration for individual Helm test filters. + properties: + exclude: + description: Exclude specifies whether the named test should be excluded. + type: boolean + name: + description: Name is the name of the test. + maxLength: 253 + minLength: 1 + type: string + required: + - name + type: object + type: array + ignoreFailures: + description: |- + IgnoreFailures tells the controller to skip remediation when the Helm tests + are run but fail. Can be overwritten for tests run after install or upgrade + actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation during + the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like Jobs + for hooks) during the performance of a Helm action. Defaults to '5m0s'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + uninstall: + description: Uninstall holds the configuration for Helm uninstall actions for this HelmRelease. + properties: + deletionPropagation: + default: background + description: |- + DeletionPropagation specifies the deletion propagation policy when + a Helm uninstall is performed. + enum: + - background + - foreground + - orphan + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables waiting for all the resources to be deleted after + a Helm uninstall is performed. + type: boolean + keepHistory: + description: |- + KeepHistory tells Helm to remove all associated resources and mark the + release as deleted, but retain the release history. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm uninstall action. Defaults + to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + upgrade: + description: Upgrade holds the configuration for Helm upgrade actions for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + upgrade action when it fails. + type: boolean + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Skip` and if omitted + CRDs are neither installed nor upgraded. + + Skip: do neither install nor replace (update) any CRDs. + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + By default, CRDs are not applied during Helm upgrade action. With this + option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the Helm upgrade action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm upgrade action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableSchemaValidation: + description: |- + DisableSchemaValidation prevents the Helm upgrade action from validating + the values against the JSON Schema. + type: boolean + disableTakeOwnership: + description: |- + DisableTakeOwnership disables taking ownership of existing resources + during the Helm upgrade action. Defaults to false. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + upgrade has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + upgrade has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement strategy. + type: boolean + preserveValues: + description: |- + PreserveValues will make Helm reuse the last release's values and merge in + overrides from 'Values'. Setting this flag makes the HelmRelease + non-declarative. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm upgrade + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an upgrade action but fail. + Defaults to 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using 'Strategy', is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + strategy: + description: Strategy to use for failure remediation. Defaults to 'rollback'. + enum: + - rollback + - uninstall + type: string + type: object + strategy: + description: |- + Strategy defines the upgrade strategy to use for this HelmRelease. + Defaults to 'RemediateOnFailure'. + properties: + name: + description: Name of the upgrade strategy. + enum: + - RemediateOnFailure + - RetryOnFailure + type: string + retryInterval: + description: |- + RetryInterval is the interval at which to retry a failed upgrade. + Can be used only when Name is set to RetryOnFailure. + Defaults to '5m'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + required: + - name + type: object + x-kubernetes-validations: + - message: .retryInterval can only be set when .name is 'RetryOnFailure' + rule: '!has(self.retryInterval) || self.name == ''RetryOnFailure''' + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm upgrade action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + values: + description: Values holds the values for this Helm release. + x-kubernetes-preserve-unknown-fields: true + valuesFrom: + description: |- + ValuesFrom holds references to resources containing Helm values for this HelmRelease, + and information about how they should be merged. + items: + description: |- + ValuesReference contains a reference to a resource containing Helm values, + and optionally the key they can be found at. + properties: + kind: + description: Kind of the values referent, valid values are ('Secret', 'ConfigMap'). + enum: + - Secret + - ConfigMap + type: string + name: + description: |- + Name of the values referent. Should reside in the same namespace as the + referring resource. + maxLength: 253 + minLength: 1 + type: string + optional: + description: |- + Optional marks this ValuesReference as optional. When set, a not found error + for the values reference is ignored, but any ValuesKey, TargetPath or + transient error will still result in a reconciliation failure. + type: boolean + targetPath: + description: |- + TargetPath is the YAML dot notation path the value should be merged at. When + set, the ValuesKey is expected to be a single flat value. Defaults to 'None', + which results in the values getting merged at the root. + maxLength: 250 + pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ + type: string + valuesKey: + description: |- + ValuesKey is the data key where the values.yaml or a specific value can be + found at. Defaults to 'values.yaml'. + maxLength: 253 + pattern: ^[\-._a-zA-Z0-9]+$ + type: string + required: + - kind + - name + type: object + type: array + required: + - interval + type: object + x-kubernetes-validations: + - message: either chart or chartRef must be set + rule: (has(self.chart) && !has(self.chartRef)) || (!has(self.chart) && has(self.chartRef)) + status: + default: + observedGeneration: -1 + description: HelmReleaseStatus defines the observed state of a HelmRelease. + properties: + conditions: + description: Conditions holds the conditions for the HelmRelease. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + failures: + description: |- + Failures is the reconciliation failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + helmChart: + description: |- + HelmChart is the namespaced name of the HelmChart resource created by + the controller for the HelmRelease. + type: string + history: + description: |- + History holds the history of Helm releases performed for this HelmRelease + up to the last successfully completed release. + items: + description: |- + Snapshot captures a point-in-time copy of the status information for a Helm release, + as managed by the controller. + properties: + apiVersion: + description: |- + APIVersion is the API version of the Snapshot. + Provisional: when the calculation method of the Digest field is changed, + this field will be used to distinguish between the old and new methods. + type: string + appVersion: + description: AppVersion is the chart app version of the release object in storage. + type: string + chartName: + description: ChartName is the chart name of the release object in storage. + type: string + chartVersion: + description: |- + ChartVersion is the chart version of the release object in + storage. + type: string + configDigest: + description: |- + ConfigDigest is the checksum of the config (better known as + "values") of the release object in storage. + It has the format of `:`. + type: string + deleted: + description: Deleted is when the release was deleted. + format: date-time + type: string + digest: + description: |- + Digest is the checksum of the release object in storage. + It has the format of `:`. + type: string + firstDeployed: + description: FirstDeployed is when the release was first deployed. + format: date-time + type: string + lastDeployed: + description: LastDeployed is when the release was last deployed. + format: date-time + type: string + name: + description: Name is the name of the release. + type: string + namespace: + description: Namespace is the namespace the release is deployed to. + type: string + ociDigest: + description: OCIDigest is the digest of the OCI artifact associated with the release. + type: string + status: + description: Status is the current state of the release. + type: string + testHooks: + additionalProperties: + description: |- + TestHookStatus holds the status information for a test hook as observed + to be run by the controller. + properties: + lastCompleted: + description: LastCompleted is the time the test hook last completed. + format: date-time + type: string + lastStarted: + description: LastStarted is the time the test hook was last started. + format: date-time + type: string + phase: + description: Phase the test hook was observed to be in. + type: string + type: object + description: |- + TestHooks is the list of test hooks for the release as observed to be + run by the controller. + type: object + version: + description: Version is the version of the release object in storage. + type: integer + required: + - chartName + - chartVersion + - configDigest + - digest + - firstDeployed + - lastDeployed + - name + - namespace + - status + - version + type: object + type: array + installFailures: + description: |- + InstallFailures is the install failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + lastAttemptedConfigDigest: + description: |- + LastAttemptedConfigDigest is the digest for the config (better known as + "values") of the last reconciliation attempt. + type: string + lastAttemptedGeneration: + description: |- + LastAttemptedGeneration is the last generation the controller attempted + to reconcile. + format: int64 + type: integer + lastAttemptedReleaseAction: + description: |- + LastAttemptedReleaseAction is the last release action performed for this + HelmRelease. It is used to determine the active retry or remediation + strategy. + enum: + - install + - upgrade + type: string + lastAttemptedReleaseActionDuration: + description: |- + LastAttemptedReleaseActionDuration is the duration of the last + release action performed for this HelmRelease. + type: string + lastAttemptedRevision: + description: |- + LastAttemptedRevision is the Source revision of the last reconciliation + attempt. For OCIRepository sources, the 12 first characters of the digest are + appended to the chart version e.g. "1.2.3+1234567890ab". + type: string + lastAttemptedRevisionDigest: + description: |- + LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. + This is only set for OCIRepository sources. + type: string + lastAttemptedValuesChecksum: + description: |- + LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last + reconciliation attempt. + + Deprecated: Use LastAttemptedConfigDigest instead. + type: string + lastHandledForceAt: + description: |- + LastHandledForceAt holds the value of the most recent + force request value, so a change of the annotation value + can be detected. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastHandledResetAt: + description: |- + LastHandledResetAt holds the value of the most recent reset request + value, so a change of the annotation value can be detected. + type: string + lastReleaseRevision: + description: |- + LastReleaseRevision is the revision of the last successful Helm release. + + Deprecated: Use History instead. + type: integer + observedCommonMetadataDigest: + description: |- + ObservedCommonMetadataDigest is the digest for the common metadata of + the last successful reconciliation attempt. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedPostRenderersDigest: + description: |- + ObservedPostRenderersDigest is the digest for the post-renderers of + the last successful reconciliation attempt. + type: string + storageNamespace: + description: |- + StorageNamespace is the namespace of the Helm release storage for the + current release. + maxLength: 63 + minLength: 1 + type: string + upgradeFailures: + description: |- + UpgradeFailures is the upgrade failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v2beta2 HelmRelease is deprecated, upgrade to v2 + name: v2beta2 + schema: + openAPIV3Schema: + description: HelmRelease is the Schema for the helmreleases API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: HelmReleaseSpec defines the desired state of a Helm release. + properties: + chart: + description: |- + Chart defines the template of the v1beta2.HelmChart that should be created + for this HelmRelease. + properties: + metadata: + description: ObjectMeta holds the template for metadata like labels and annotations. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + type: object + labels: + additionalProperties: + type: string + description: |- + Map of string keys and values that can be used to organize and categorize + (scope and select) objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + type: object + type: object + spec: + description: Spec holds the template for the v1beta2.HelmChartSpec for this HelmRelease. + properties: + chart: + description: The name or path the Helm chart is available at in the SourceRef. + maxLength: 2048 + minLength: 1 + type: string + ignoreMissingValuesFiles: + description: IgnoreMissingValuesFiles controls whether to silently ignore missing values files rather than failing. + type: boolean + interval: + description: |- + Interval at which to check the v1.Source for updates. Defaults to + 'HelmReleaseSpec.Interval'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + reconcileStrategy: + default: ChartVersion + description: |- + Determines what enables the creation of a new artifact. Valid values are + ('ChartVersion', 'Revision'). + See the documentation of the values for an explanation on their behavior. + Defaults to ChartVersion when omitted. + enum: + - ChartVersion + - Revision + type: string + sourceRef: + description: The name and namespace of the v1.Source the chart is available at. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - HelmRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent. + maxLength: 63 + minLength: 1 + type: string + required: + - kind + - name + type: object + valuesFile: + description: |- + Alternative values file to use as the default chart values, expected to + be a relative path in the SourceRef. Deprecated in favor of ValuesFiles, + for backwards compatibility the file defined here is merged before the + ValuesFiles items. Ignored when omitted. + type: string + valuesFiles: + description: |- + Alternative list of values files to use as the chart values (values.yaml + is not included by default), expected to be a relative path in the SourceRef. + Values files are merged in the order of this list with the last file overriding + the first. Ignored when omitted. + items: + type: string + type: array + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + This field is only supported for OCI sources. + Chart dependencies, which are not bundled in the umbrella chart artifact, + are not verified. + properties: + provider: + default: cosign + description: Provider specifies the technology used to sign the OCI Helm chart. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + version: + default: '*' + description: |- + Version semver expression, ignored for charts from v1beta2.GitRepository and + v1beta2.Bucket sources. Defaults to latest when omitted. + type: string + required: + - chart + - sourceRef + type: object + required: + - spec + type: object + chartRef: + description: |- + ChartRef holds a reference to a source controller resource containing the + Helm chart artifact. + + Note: this field is provisional to the v2 API, and not actively used + by v2beta2 HelmReleases. + properties: + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - HelmChart + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kubernetes + resource object that contains the reference. + maxLength: 63 + minLength: 1 + type: string + required: + - kind + - name + type: object + dependsOn: + description: |- + DependsOn may contain a meta.NamespacedObjectReference slice with + references to HelmRelease resources that must be ready before this HelmRelease + can be reconciled. + items: + description: |- + NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any + namespace. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it acts as LocalObjectReference. + type: string + required: + - name + type: object + type: array + driftDetection: + description: |- + DriftDetection holds the configuration for detecting and handling + differences between the manifest in the Helm storage and the resources + currently existing in the cluster. + properties: + ignore: + description: |- + Ignore contains a list of rules for specifying which changes to ignore + during diffing. + items: + description: |- + IgnoreRule defines a rule to selectively disregard specific changes during + the drift detection process. + properties: + paths: + description: |- + Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from + consideration in a Kubernetes object. + items: + type: string + type: array + target: + description: |- + Target is a selector for specifying Kubernetes objects to which this + rule applies. + If Target is not set, the Paths will be ignored for all Kubernetes + objects within the manifest of the Helm release. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - paths + type: object + type: array + mode: + description: |- + Mode defines how differences should be handled between the Helm manifest + and the manifest currently applied to the cluster. + If not explicitly set, it defaults to DiffModeDisabled. + enum: + - enabled + - warn + - disabled + type: string + type: object + install: + description: Install holds the configuration for Helm install actions for this HelmRelease. + properties: + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Create` and if omitted + CRDs are installed but not updated. + + Skip: do neither install nor replace (update) any CRDs. + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + By default, CRDs are applied (installed) during Helm install action. + With this option users can opt in to CRD replace existing CRDs on Helm + install actions, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + createNamespace: + description: |- + CreateNamespace tells the Helm install action to create the + HelmReleaseSpec.TargetNamespace if it does not exist yet. + On uninstall, the namespace will not be garbage collected. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the Helm install action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm install action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + install has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + install has been performed. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm install + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an install action but fail. Defaults to + 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false'. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using an uninstall, is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + type: object + replace: + description: |- + Replace tells the Helm install action to re-use the 'ReleaseName', but only + if that name is a deleted release which remains in the history. + type: boolean + skipCRDs: + description: |- + SkipCRDs tells the Helm install action to not install any CRDs. By default, + CRDs are installed if not already present. + + Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm install action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + interval: + description: Interval at which to reconcile the Helm release. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: |- + KubeConfig for reconciling the HelmRelease on a remote cluster. + When used in combination with HelmReleaseSpec.ServiceAccountName, + forces the controller to act on behalf of that Service Account at the + target cluster. + If the --default-service-account flag is set, its value will be used as + a controller level fallback for when HelmReleaseSpec.ServiceAccountName + is empty. + properties: + configMapRef: + description: |- + ConfigMapRef holds an optional name of a ConfigMap that contains + the following keys: + + - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or + `generic`. Required. + - `cluster`: the fully qualified resource name of the Kubernetes + cluster in the cloud provider API. Not used by the `generic` + provider. Required when one of `address` or `ca.crt` is not set. + - `address`: the address of the Kubernetes API server. Required + for `generic`. For the other providers, if not specified, the + first address in the cluster resource will be used, and if + specified, it must match one of the addresses in the cluster + resource. + If audiences is not set, will be used as the audience for the + `generic` provider. + - `ca.crt`: the optional PEM-encoded CA certificate for the + Kubernetes API server. If not set, the controller will use the + CA certificate from the cluster resource. + - `audiences`: the optional audiences as a list of + line-break-separated strings for the Kubernetes ServiceAccount + token. Defaults to the `address` for the `generic` provider, or + to specific values for the other providers depending on the + provider. + - `serviceAccountName`: the optional name of the Kubernetes + ServiceAccount in the same namespace that should be used + for authentication. If not specified, the controller + ServiceAccount will be used. + + Mutually exclusive with SecretRef. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef holds an optional name of a secret that contains a key with + the kubeconfig file as the value. If no key is set, the key will default + to 'value'. Mutually exclusive with ConfigMapRef. + It is recommended that the kubeconfig is self-contained, and the secret + is regularly updated if credentials such as a cloud-access-token expire. + Cloud specific `cmd-path` auth helpers will not function without adding + binaries and credentials to the Pod that is responsible for reconciling + Kubernetes resources. Supported only for the generic provider. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + x-kubernetes-validations: + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified + rule: has(self.configMapRef) || has(self.secretRef) + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified + rule: '!has(self.configMapRef) || !has(self.secretRef)' + maxHistory: + description: |- + MaxHistory is the number of revisions saved by Helm for this HelmRelease. + Use '0' for an unlimited number of revisions; defaults to '5'. + type: integer + persistentClient: + description: |- + PersistentClient tells the controller to use a persistent Kubernetes + client for this release. When enabled, the client will be reused for the + duration of the reconciliation, instead of being created and destroyed + for each (step of a) Helm action. + + This can improve performance, but may cause issues with some Helm charts + that for example do create Custom Resource Definitions during installation + outside Helm's CRD lifecycle hooks, which are then not observed to be + available by e.g. post-install hooks. + + If not set, it defaults to true. + type: boolean + postRenderers: + description: |- + PostRenderers holds an array of Helm PostRenderers, which will be applied in order + of their definition. + items: + description: PostRenderer contains a Helm PostRenderer specification. + properties: + kustomize: + description: Kustomization to apply as PostRenderer. + properties: + images: + description: |- + Images is a list of (image name, new name, new tag or digest) + for changing image names, tags or digests. This can also be achieved with a + patch, but this operator is simpler to specify. + items: + description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag. + properties: + digest: + description: |- + Digest is the value used to replace the original image tag. + If digest is present NewTag value is ignored. + type: string + name: + description: Name is a tag-less image name. + type: string + newName: + description: NewName is the value used to replace the original name. + type: string + newTag: + description: NewTag is the value used to replace the original tag. + type: string + required: + - name + type: object + type: array + patches: + description: |- + Strategic merge and JSON patches, defined as inline YAML objects, + capable of targeting objects based on kind, label and annotation selectors. + items: + description: |- + Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should + be applied to. + properties: + patch: + description: |- + Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with + an array of operation objects. + type: string + target: + description: Target points to the resources that the patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + type: object + type: array + patchesJson6902: + description: |- + JSON 6902 patches, defined as inline YAML objects. + + Deprecated: use Patches instead. + items: + description: JSON6902Patch contains a JSON6902 patch and the target the patch should be applied to. + properties: + patch: + description: Patch contains the JSON6902 patch document with an array of operation objects. + items: + description: |- + JSON6902 is a JSON6902 operation object. + https://datatracker.ietf.org/doc/html/rfc6902#section-4 + properties: + from: + description: |- + From contains a JSON-pointer value that references a location within the target document where the operation is + performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations. + type: string + op: + description: |- + Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or + "test". + https://datatracker.ietf.org/doc/html/rfc6902#section-4 + enum: + - test + - remove + - add + - replace + - move + - copy + type: string + path: + description: |- + Path contains the JSON-pointer value that references a location within the target document where the operation + is performed. The meaning of the value depends on the value of Op. + type: string + value: + description: |- + Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into + account by all operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + target: + description: Target points to the resources that the patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + - target + type: object + type: array + patchesStrategicMerge: + description: |- + Strategic merge patches, defined as inline YAML objects. + + Deprecated: use Patches instead. + items: + x-kubernetes-preserve-unknown-fields: true + type: array + type: object + type: object + type: array + releaseName: + description: |- + ReleaseName used for the Helm release. Defaults to a composition of + '[TargetNamespace-]Name'. + maxLength: 53 + minLength: 1 + type: string + rollback: + description: Rollback holds the configuration for Helm rollback actions for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + rollback action when it fails. + type: boolean + disableHooks: + description: DisableHooks prevents hooks from running during the Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + rollback has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + rollback has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement strategy. + type: boolean + recreate: + description: Recreate performs pod restarts for the resource if applicable. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm rollback action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + serviceAccountName: + description: |- + The name of the Kubernetes service account to impersonate + when reconciling this HelmRelease. + maxLength: 253 + minLength: 1 + type: string + storageNamespace: + description: |- + StorageNamespace used for the Helm storage. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + suspend: + description: |- + Suspend tells the controller to suspend reconciliation for this HelmRelease, + it does not apply to already started reconciliations. Defaults to false. + type: boolean + targetNamespace: + description: |- + TargetNamespace to target when performing operations for the HelmRelease. + Defaults to the namespace of the HelmRelease. + maxLength: 63 + minLength: 1 + type: string + test: + description: Test holds the configuration for Helm test actions for this HelmRelease. + properties: + enable: + description: |- + Enable enables Helm test actions for this HelmRelease after an Helm install + or upgrade action has been performed. + type: boolean + filters: + description: Filters is a list of tests to run or exclude from running. + items: + description: Filter holds the configuration for individual Helm test filters. + properties: + exclude: + description: Exclude specifies whether the named test should be excluded. + type: boolean + name: + description: Name is the name of the test. + maxLength: 253 + minLength: 1 + type: string + required: + - name + type: object + type: array + ignoreFailures: + description: |- + IgnoreFailures tells the controller to skip remediation when the Helm tests + are run but fail. Can be overwritten for tests run after install or upgrade + actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation during + the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like Jobs + for hooks) during the performance of a Helm action. Defaults to '5m0s'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + uninstall: + description: Uninstall holds the configuration for Helm uninstall actions for this HelmRelease. + properties: + deletionPropagation: + default: background + description: |- + DeletionPropagation specifies the deletion propagation policy when + a Helm uninstall is performed. + enum: + - background + - foreground + - orphan + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the Helm rollback action. + type: boolean + disableWait: + description: |- + DisableWait disables waiting for all the resources to be deleted after + a Helm uninstall is performed. + type: boolean + keepHistory: + description: |- + KeepHistory tells Helm to remove all associated resources and mark the + release as deleted, but retain the release history. + type: boolean + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm uninstall action. Defaults + to 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + upgrade: + description: Upgrade holds the configuration for Helm upgrade actions for this HelmRelease. + properties: + cleanupOnFail: + description: |- + CleanupOnFail allows deletion of new resources created during the Helm + upgrade action when it fails. + type: boolean + crds: + description: |- + CRDs upgrade CRDs from the Helm Chart's crds directory according + to the CRD upgrade policy provided here. Valid values are `Skip`, + `Create` or `CreateReplace`. Default is `Skip` and if omitted + CRDs are neither installed nor upgraded. + + Skip: do neither install nor replace (update) any CRDs. + + Create: new CRDs are created, existing CRDs are neither updated nor deleted. + + CreateReplace: new CRDs are created, existing CRDs are updated (replaced) + but not deleted. + + By default, CRDs are not applied during Helm upgrade action. With this + option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. + https://helm.sh/docs/chart_best_practices/custom_resource_definitions. + enum: + - Skip + - Create + - CreateReplace + type: string + disableHooks: + description: DisableHooks prevents hooks from running during the Helm upgrade action. + type: boolean + disableOpenAPIValidation: + description: |- + DisableOpenAPIValidation prevents the Helm upgrade action from validating + rendered templates against the Kubernetes OpenAPI Schema. + type: boolean + disableWait: + description: |- + DisableWait disables the waiting for resources to be ready after a Helm + upgrade has been performed. + type: boolean + disableWaitForJobs: + description: |- + DisableWaitForJobs disables waiting for jobs to complete after a Helm + upgrade has been performed. + type: boolean + force: + description: Force forces resource updates through a replacement strategy. + type: boolean + preserveValues: + description: |- + PreserveValues will make Helm reuse the last release's values and merge in + overrides from 'Values'. Setting this flag makes the HelmRelease + non-declarative. + type: boolean + remediation: + description: |- + Remediation holds the remediation configuration for when the Helm upgrade + action for the HelmRelease fails. The default is to not perform any action. + properties: + ignoreTestFailures: + description: |- + IgnoreTestFailures tells the controller to skip remediation when the Helm + tests are run after an upgrade action but fail. + Defaults to 'Test.IgnoreFailures'. + type: boolean + remediateLastFailure: + description: |- + RemediateLastFailure tells the controller to remediate the last failure, when + no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. + type: boolean + retries: + description: |- + Retries is the number of retries that should be attempted on failures before + bailing. Remediation, using 'Strategy', is performed between each attempt. + Defaults to '0', a negative integer equals to unlimited retries. + type: integer + strategy: + description: Strategy to use for failure remediation. Defaults to 'rollback'. + enum: + - rollback + - uninstall + type: string + type: object + timeout: + description: |- + Timeout is the time to wait for any individual Kubernetes operation (like + Jobs for hooks) during the performance of a Helm upgrade action. Defaults to + 'HelmReleaseSpec.Timeout'. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + type: object + values: + description: Values holds the values for this Helm release. + x-kubernetes-preserve-unknown-fields: true + valuesFrom: + description: |- + ValuesFrom holds references to resources containing Helm values for this HelmRelease, + and information about how they should be merged. + items: + description: |- + ValuesReference contains a reference to a resource containing Helm values, + and optionally the key they can be found at. + properties: + kind: + description: Kind of the values referent, valid values are ('Secret', 'ConfigMap'). + enum: + - Secret + - ConfigMap + type: string + name: + description: |- + Name of the values referent. Should reside in the same namespace as the + referring resource. + maxLength: 253 + minLength: 1 + type: string + optional: + description: |- + Optional marks this ValuesReference as optional. When set, a not found error + for the values reference is ignored, but any ValuesKey, TargetPath or + transient error will still result in a reconciliation failure. + type: boolean + targetPath: + description: |- + TargetPath is the YAML dot notation path the value should be merged at. When + set, the ValuesKey is expected to be a single flat value. Defaults to 'None', + which results in the values getting merged at the root. + maxLength: 250 + pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ + type: string + valuesKey: + description: |- + ValuesKey is the data key where the values.yaml or a specific value can be + found at. Defaults to 'values.yaml'. + maxLength: 253 + pattern: ^[\-._a-zA-Z0-9]+$ + type: string + required: + - kind + - name + type: object + type: array + required: + - interval + type: object + x-kubernetes-validations: + - message: either chart or chartRef must be set + rule: (has(self.chart) && !has(self.chartRef)) || (!has(self.chart) && has(self.chartRef)) + status: + default: + observedGeneration: -1 + description: HelmReleaseStatus defines the observed state of a HelmRelease. + properties: + conditions: + description: Conditions holds the conditions for the HelmRelease. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + failures: + description: |- + Failures is the reconciliation failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + helmChart: + description: |- + HelmChart is the namespaced name of the HelmChart resource created by + the controller for the HelmRelease. + type: string + history: + description: |- + History holds the history of Helm releases performed for this HelmRelease + up to the last successfully completed release. + items: + description: |- + Snapshot captures a point-in-time copy of the status information for a Helm release, + as managed by the controller. + properties: + apiVersion: + description: |- + APIVersion is the API version of the Snapshot. + Provisional: when the calculation method of the Digest field is changed, + this field will be used to distinguish between the old and new methods. + type: string + appVersion: + description: AppVersion is the chart app version of the release object in storage. + type: string + chartName: + description: ChartName is the chart name of the release object in storage. + type: string + chartVersion: + description: |- + ChartVersion is the chart version of the release object in + storage. + type: string + configDigest: + description: |- + ConfigDigest is the checksum of the config (better known as + "values") of the release object in storage. + It has the format of `:`. + type: string + deleted: + description: Deleted is when the release was deleted. + format: date-time + type: string + digest: + description: |- + Digest is the checksum of the release object in storage. + It has the format of `:`. + type: string + firstDeployed: + description: FirstDeployed is when the release was first deployed. + format: date-time + type: string + lastDeployed: + description: LastDeployed is when the release was last deployed. + format: date-time + type: string + name: + description: Name is the name of the release. + type: string + namespace: + description: Namespace is the namespace the release is deployed to. + type: string + ociDigest: + description: OCIDigest is the digest of the OCI artifact associated with the release. + type: string + status: + description: Status is the current state of the release. + type: string + testHooks: + additionalProperties: + description: |- + TestHookStatus holds the status information for a test hook as observed + to be run by the controller. + properties: + lastCompleted: + description: LastCompleted is the time the test hook last completed. + format: date-time + type: string + lastStarted: + description: LastStarted is the time the test hook was last started. + format: date-time + type: string + phase: + description: Phase the test hook was observed to be in. + type: string + type: object + description: |- + TestHooks is the list of test hooks for the release as observed to be + run by the controller. + type: object + version: + description: Version is the version of the release object in storage. + type: integer + required: + - chartName + - chartVersion + - configDigest + - digest + - firstDeployed + - lastDeployed + - name + - namespace + - status + - version + type: object + type: array + installFailures: + description: |- + InstallFailures is the install failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + lastAppliedRevision: + description: |- + LastAppliedRevision is the revision of the last successfully applied + source. + + Deprecated: the revision can now be found in the History. + type: string + lastAttemptedConfigDigest: + description: |- + LastAttemptedConfigDigest is the digest for the config (better known as + "values") of the last reconciliation attempt. + type: string + lastAttemptedGeneration: + description: |- + LastAttemptedGeneration is the last generation the controller attempted + to reconcile. + format: int64 + type: integer + lastAttemptedReleaseAction: + description: |- + LastAttemptedReleaseAction is the last release action performed for this + HelmRelease. It is used to determine the active remediation strategy. + enum: + - install + - upgrade + type: string + lastAttemptedRevision: + description: |- + LastAttemptedRevision is the Source revision of the last reconciliation + attempt. For OCIRepository sources, the 12 first characters of the digest are + appended to the chart version e.g. "1.2.3+1234567890ab". + type: string + lastAttemptedRevisionDigest: + description: |- + LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. + This is only set for OCIRepository sources. + type: string + lastAttemptedValuesChecksum: + description: |- + LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last + reconciliation attempt. + + Deprecated: Use LastAttemptedConfigDigest instead. + type: string + lastHandledForceAt: + description: |- + LastHandledForceAt holds the value of the most recent force request + value, so a change of the annotation value can be detected. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastHandledResetAt: + description: |- + LastHandledResetAt holds the value of the most recent reset request + value, so a change of the annotation value can be detected. + type: string + lastReleaseRevision: + description: |- + LastReleaseRevision is the revision of the last successful Helm release. + + Deprecated: Use History instead. + type: integer + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedPostRenderersDigest: + description: |- + ObservedPostRenderersDigest is the digest for the post-renderers of + the last successful reconciliation attempt. + type: string + storageNamespace: + description: |- + StorageNamespace is the namespace of the Helm release storage for the + current release. + maxLength: 63 + minLength: 1 + type: string + upgradeFailures: + description: |- + UpgradeFailures is the upgrade failure count against the latest desired + state. It is reset after a successful reconciliation. + format: int64 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: helmrepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: HelmRepository + listKind: HelmRepositoryList + plural: helmrepositories + shortNames: + - helmrepo + singular: helmrepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: HelmRepository is the Schema for the helmrepositories API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + HelmRepositorySpec specifies the required configuration to produce an + Artifact for a Helm repository index YAML. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + It takes precedence over the values specified in the Secret referred + to by `.spec.secretRef`. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + insecure: + description: |- + Insecure allows connecting to a non-TLS HTTP container registry. + This field is only taken into account if the .spec.type field is set to 'oci'. + type: boolean + interval: + description: |- + Interval at which the HelmRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + passCredentials: + description: |- + PassCredentials allows the credentials from the SecretRef to be passed + on to a host that does not match the host as defined in URL. + This may be required if the host of the advertised chart URLs in the + index differ from the defined URL. + Enabling this should be done with caution, as it can potentially result + in credentials getting stolen in a MITM-attack. + type: boolean + provider: + default: generic + description: |- + Provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + This field is optional, and only taken into account if the .spec.type field is set to 'oci'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the HelmRepository. + For HTTP/S basic auth the secret must contain 'username' and 'password' + fields. + Support for TLS auth using the 'certFile' and 'keyFile', and/or 'caFile' + keys is deprecated. Please use `.spec.certSecretRef` instead. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + HelmRepository. + type: boolean + timeout: + description: |- + Timeout is used for the index fetch operation for an HTTPS helm repository, + and for remote OCI Repository operations like pulling for an OCI helm + chart by the associated HelmChart. + Its default value is 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + type: + description: |- + Type of the HelmRepository. + When this field is set to "oci", the URL field value must be prefixed with "oci://". + enum: + - default + - oci + type: string + url: + description: |- + URL of the Helm repository, a valid URL contains at least a protocol and + host. + pattern: ^(http|https|oci)://.*$ + type: string + required: + - url + type: object + status: + default: + observedGeneration: -1 + description: HelmRepositoryStatus records the observed state of the HelmRepository. + properties: + artifact: + description: Artifact represents the last successful HelmRepository reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmRepository. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the HelmRepository + object. + format: int64 + type: integer + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + HelmRepositoryStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 HelmRepository is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: HelmRepository is the Schema for the helmrepositories API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + HelmRepositorySpec specifies the required configuration to produce an + Artifact for a Helm repository index YAML. + properties: + accessFrom: + description: |- + AccessFrom specifies an Access Control List for allowing cross-namespace + references to this object. + NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + It takes precedence over the values specified in the Secret referred + to by `.spec.secretRef`. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + insecure: + description: |- + Insecure allows connecting to a non-TLS HTTP container registry. + This field is only taken into account if the .spec.type field is set to 'oci'. + type: boolean + interval: + description: |- + Interval at which the HelmRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + passCredentials: + description: |- + PassCredentials allows the credentials from the SecretRef to be passed + on to a host that does not match the host as defined in URL. + This may be required if the host of the advertised chart URLs in the + index differ from the defined URL. + Enabling this should be done with caution, as it can potentially result + in credentials getting stolen in a MITM-attack. + type: boolean + provider: + default: generic + description: |- + Provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + This field is optional, and only taken into account if the .spec.type field is set to 'oci'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing authentication credentials + for the HelmRepository. + For HTTP/S basic auth the secret must contain 'username' and 'password' + fields. + Support for TLS auth using the 'certFile' and 'keyFile', and/or 'caFile' + keys is deprecated. Please use `.spec.certSecretRef` instead. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend the reconciliation of this + HelmRepository. + type: boolean + timeout: + description: |- + Timeout is used for the index fetch operation for an HTTPS helm repository, + and for remote OCI Repository operations like pulling for an OCI helm + chart by the associated HelmChart. + Its default value is 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + type: + description: |- + Type of the HelmRepository. + When this field is set to "oci", the URL field value must be prefixed with "oci://". + enum: + - default + - oci + type: string + url: + description: |- + URL of the Helm repository, a valid URL contains at least a protocol and + host. + pattern: ^(http|https|oci)://.*$ + type: string + required: + - url + type: object + status: + default: + observedGeneration: -1 + description: HelmRepositoryStatus records the observed state of the HelmRepository. + properties: + artifact: + description: Artifact represents the last successful HelmRepository reconciliation. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the HelmRepository. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: |- + ObservedGeneration is the last observed generation of the HelmRepository + object. + format: int64 + type: integer + url: + description: |- + URL is the dynamic fetch link for the latest Artifact. + It is provided on a "best effort" basis, and using the precise + HelmRepositoryStatus.Artifact data is recommended. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: imagepolicies.image.toolkit.fluxcd.io +spec: + group: image.toolkit.fluxcd.io + names: + kind: ImagePolicy + listKind: ImagePolicyList + plural: imagepolicies + shortNames: + - imgpol + - imagepol + singular: imagepolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.latestRef.name + name: Image + type: string + - jsonPath: .status.latestRef.tag + name: Tag + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ImagePolicy is the Schema for the imagepolicies API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImagePolicySpec defines the parameters for calculating the + ImagePolicy. + properties: + digestReflectionPolicy: + default: Never + description: |- + DigestReflectionPolicy governs the setting of the `.status.latestRef.digest` field. + + Never: The digest field will always be set to the empty string. + + IfNotPresent: The digest field will be set to the digest of the elected + latest image if the field is empty and the image did not change. + + Always: The digest field will always be set to the digest of the elected + latest image. + + Default: Never. + enum: + - Always + - IfNotPresent + - Never + type: string + filterTags: + description: |- + FilterTags enables filtering for only a subset of tags based on a set of + rules. If no rules are provided, all the tags from the repository will be + ordered and compared. + properties: + extract: + description: |- + Extract allows a capture group to be extracted from the specified regular + expression pattern, useful before tag evaluation. + type: string + pattern: + description: |- + Pattern specifies a regular expression pattern used to filter for image + tags. + type: string + type: object + imageRepositoryRef: + description: |- + ImageRepositoryRef points at the object specifying the image + being scanned + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it acts as LocalObjectReference. + type: string + required: + - name + type: object + interval: + description: |- + Interval is the length of time to wait between + refreshing the digest of the latest tag when the + reflection policy is set to "Always". + + Defaults to 10m. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + policy: + description: |- + Policy gives the particulars of the policy to be followed in + selecting the most recent image + properties: + alphabetical: + description: Alphabetical set of rules to use for alphabetical ordering of the tags. + properties: + order: + default: asc + description: |- + Order specifies the sorting order of the tags. Given the letters of the + alphabet as tags, ascending order would select Z, and descending order + would select A. + enum: + - asc + - desc + type: string + type: object + numerical: + description: Numerical set of rules to use for numerical ordering of the tags. + properties: + order: + default: asc + description: |- + Order specifies the sorting order of the tags. Given the integer values + from 0 to 9 as tags, ascending order would select 9, and descending order + would select 0. + enum: + - asc + - desc + type: string + type: object + semver: + description: |- + SemVer gives a semantic version range to check against the tags + available. + properties: + range: + description: |- + Range gives a semver range for the image tag; the highest + version within the range that's a tag yields the latest image. + type: string + required: + - range + type: object + type: object + suspend: + description: |- + This flag tells the controller to suspend subsequent policy reconciliations. + It does not apply to already started reconciliations. Defaults to false. + type: boolean + required: + - imageRepositoryRef + - policy + type: object + x-kubernetes-validations: + - message: spec.interval is only accepted when spec.digestReflectionPolicy is set to 'Always' + rule: '!has(self.interval) || (has(self.digestReflectionPolicy) && self.digestReflectionPolicy == ''Always'')' + - message: spec.interval must be set when spec.digestReflectionPolicy is set to 'Always' + rule: has(self.interval) || !has(self.digestReflectionPolicy) || self.digestReflectionPolicy != 'Always' + status: + default: + observedGeneration: -1 + description: ImagePolicyStatus defines the observed state of ImagePolicy + properties: + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + latestRef: + description: |- + LatestRef gives the first in the list of images scanned by + the image repository, when filtered and ordered according + to the policy. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + observedGeneration: + format: int64 + type: integer + observedPreviousRef: + description: |- + ObservedPreviousRef is the observed previous LatestRef. It is used + to keep track of the previous and current images. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.latestRef.name + name: Image + type: string + - jsonPath: .status.latestRef.tag + name: Tag + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta2 ImagePolicy is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: ImagePolicy is the Schema for the imagepolicies API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImagePolicySpec defines the parameters for calculating the + ImagePolicy. + properties: + digestReflectionPolicy: + default: Never + description: |- + DigestReflectionPolicy governs the setting of the `.status.latestRef.digest` field. + + Never: The digest field will always be set to the empty string. + + IfNotPresent: The digest field will be set to the digest of the elected + latest image if the field is empty and the image did not change. + + Always: The digest field will always be set to the digest of the elected + latest image. + + Default: Never. + enum: + - Always + - IfNotPresent + - Never + type: string + filterTags: + description: |- + FilterTags enables filtering for only a subset of tags based on a set of + rules. If no rules are provided, all the tags from the repository will be + ordered and compared. + properties: + extract: + description: |- + Extract allows a capture group to be extracted from the specified regular + expression pattern, useful before tag evaluation. + type: string + pattern: + description: |- + Pattern specifies a regular expression pattern used to filter for image + tags. + type: string + type: object + imageRepositoryRef: + description: |- + ImageRepositoryRef points at the object specifying the image + being scanned + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it acts as LocalObjectReference. + type: string + required: + - name + type: object + interval: + description: |- + Interval is the length of time to wait between + refreshing the digest of the latest tag when the + reflection policy is set to "Always". + + Defaults to 10m. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + policy: + description: |- + Policy gives the particulars of the policy to be followed in + selecting the most recent image + properties: + alphabetical: + description: Alphabetical set of rules to use for alphabetical ordering of the tags. + properties: + order: + default: asc + description: |- + Order specifies the sorting order of the tags. Given the letters of the + alphabet as tags, ascending order would select Z, and descending order + would select A. + enum: + - asc + - desc + type: string + type: object + numerical: + description: Numerical set of rules to use for numerical ordering of the tags. + properties: + order: + default: asc + description: |- + Order specifies the sorting order of the tags. Given the integer values + from 0 to 9 as tags, ascending order would select 9, and descending order + would select 0. + enum: + - asc + - desc + type: string + type: object + semver: + description: |- + SemVer gives a semantic version range to check against the tags + available. + properties: + range: + description: |- + Range gives a semver range for the image tag; the highest + version within the range that's a tag yields the latest image. + type: string + required: + - range + type: object + type: object + suspend: + description: |- + This flag tells the controller to suspend subsequent policy reconciliations. + It does not apply to already started reconciliations. Defaults to false. + type: boolean + required: + - imageRepositoryRef + - policy + type: object + x-kubernetes-validations: + - message: spec.interval is only accepted when spec.digestReflectionPolicy is set to 'Always' + rule: '!has(self.interval) || (has(self.digestReflectionPolicy) && self.digestReflectionPolicy == ''Always'')' + - message: spec.interval must be set when spec.digestReflectionPolicy is set to 'Always' + rule: has(self.interval) || !has(self.digestReflectionPolicy) || self.digestReflectionPolicy != 'Always' + status: + default: + observedGeneration: -1 + description: ImagePolicyStatus defines the observed state of ImagePolicy + properties: + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + latestRef: + description: |- + LatestRef gives the first in the list of images scanned by + the image repository, when filtered and ordered according + to the policy. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + observedGeneration: + format: int64 + type: integer + observedPreviousRef: + description: |- + ObservedPreviousRef is the observed previous LatestRef. It is used + to keep track of the previous and current images. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: imagerepositories.image.toolkit.fluxcd.io +spec: + group: image.toolkit.fluxcd.io + names: + kind: ImageRepository + listKind: ImageRepositoryList + plural: imagerepositories + shortNames: + - imgrepo + - imagerepo + singular: imagerepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.image + name: Image + type: string + - jsonPath: .status.lastScanResult.tagCount + name: Tags + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .status.lastScanResult.scanTime + name: Last scan + priority: 1 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ImageRepository is the Schema for the imagerepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImageRepositorySpec defines the parameters for scanning an image + repository, e.g., `fluxcd/flux`. + properties: + accessFrom: + description: |- + AccessFrom defines an ACL for allowing cross-namespace references + to the ImageRepository object based on the caller's namespace labels. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + Note: Support for the `caFile`, `certFile` and `keyFile` keys has + been deprecated. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + exclusionList: + default: + - ^.*\.sig$ + description: |- + ExclusionList is a list of regex strings used to exclude certain tags + from being stored in the database. + items: + type: string + maxItems: 25 + type: array + image: + description: Image is the name of the image repository + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container registry. + type: boolean + interval: + description: |- + Interval is the length of time to wait between + scans of the image repository. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + provider: + default: generic + description: |- + The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the container registry. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef can be given the name of a secret containing + credentials to use for the image registry. The secret should be + created with `kubectl create secret docker-registry`, or the + equivalent. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the image pull if the service account has attached pull secrets. + maxLength: 253 + type: string + suspend: + description: |- + This flag tells the controller to suspend subsequent image scans. + It does not apply to already started scans. Defaults to false. + type: boolean + timeout: + description: |- + Timeout for image scanning. + Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + required: + - image + - interval + type: object + status: + default: + observedGeneration: -1 + description: ImageRepositoryStatus defines the observed state of ImageRepository + properties: + canonicalImageName: + description: |- + CanonicalName is the name of the image repository with all the + implied bits made explicit; e.g., `docker.io/library/alpine` + rather than `alpine`. + type: string + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastScanResult: + description: LastScanResult contains the number of fetched tags. + properties: + latestTags: + description: |- + LatestTags is a small sample of the tags found in the last scan. + It's the first 10 tags when sorting all the tags in descending + alphabetical order. + items: + type: string + type: array + revision: + description: Revision is a stable hash of the scanned tags. + type: string + scanTime: + description: ScanTime is the time when the last scan was performed. + format: date-time + type: string + tagCount: + description: TagCount is the number of tags found in the last scan. + type: integer + required: + - tagCount + type: object + observedExclusionList: + description: |- + ObservedExclusionList is a list of observed exclusion list. It reflects + the exclusion rules used for the observed scan result in + spec.lastScanResult. + items: + type: string + type: array + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.image + name: Image + type: string + - jsonPath: .status.lastScanResult.tagCount + name: Tags + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .status.lastScanResult.scanTime + name: Last scan + priority: 1 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta2 ImageRepository is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: ImageRepository is the Schema for the imagerepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ImageRepositorySpec defines the parameters for scanning an image + repository, e.g., `fluxcd/flux`. + properties: + accessFrom: + description: |- + AccessFrom defines an ACL for allowing cross-namespace references + to the ImageRepository object based on the caller's namespace labels. + properties: + namespaceSelectors: + description: |- + NamespaceSelectors is the list of namespace selectors to which this ACL applies. + Items in this list are evaluated using a logical OR operation. + items: + description: |- + NamespaceSelector selects the namespaces to which this ACL applies. + An empty map of MatchLabels matches all namespaces in a cluster. + properties: + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + type: array + required: + - namespaceSelectors + type: object + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + Note: Support for the `caFile`, `certFile` and `keyFile` keys has + been deprecated. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + exclusionList: + default: + - ^.*\.sig$ + description: |- + ExclusionList is a list of regex strings used to exclude certain tags + from being stored in the database. + items: + type: string + maxItems: 25 + type: array + image: + description: Image is the name of the image repository + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container registry. + type: boolean + interval: + description: |- + Interval is the length of time to wait between + scans of the image repository. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + provider: + default: generic + description: |- + The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the container registry. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef can be given the name of a secret containing + credentials to use for the image registry. The secret should be + created with `kubectl create secret docker-registry`, or the + equivalent. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the image pull if the service account has attached pull secrets. + maxLength: 253 + type: string + suspend: + description: |- + This flag tells the controller to suspend subsequent image scans. + It does not apply to already started scans. Defaults to false. + type: boolean + timeout: + description: |- + Timeout for image scanning. + Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + required: + - image + - interval + type: object + status: + default: + observedGeneration: -1 + description: ImageRepositoryStatus defines the observed state of ImageRepository + properties: + canonicalImageName: + description: |- + CanonicalName is the name of the image repository with all the + implied bits made explicit; e.g., `docker.io/library/alpine` + rather than `alpine`. + type: string + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastScanResult: + description: LastScanResult contains the number of fetched tags. + properties: + latestTags: + description: |- + LatestTags is a small sample of the tags found in the last scan. + It's the first 10 tags when sorting all the tags in descending + alphabetical order. + items: + type: string + type: array + revision: + description: Revision is a stable hash of the scanned tags. + type: string + scanTime: + description: ScanTime is the time when the last scan was performed. + format: date-time + type: string + tagCount: + description: TagCount is the number of tags found in the last scan. + type: integer + required: + - tagCount + type: object + observedExclusionList: + description: |- + ObservedExclusionList is a list of observed exclusion list. It reflects + the exclusion rules used for the observed scan result in + spec.lastScanResult. + items: + type: string + type: array + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: imageupdateautomations.image.toolkit.fluxcd.io +spec: + group: image.toolkit.fluxcd.io + names: + kind: ImageUpdateAutomation + listKind: ImageUpdateAutomationList + plural: imageupdateautomations + shortNames: + - iua + - imgupd + - imgauto + singular: imageupdateautomation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .status.lastAutomationRunTime + name: Last run + priority: 1 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: ImageUpdateAutomation is the Schema for the imageupdateautomations API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation + properties: + git: + description: |- + GitSpec contains all the git-specific definitions. This is + technically optional, but in practice mandatory until there are + other kinds of source allowed. + properties: + checkout: + description: |- + Checkout gives the parameters for cloning the git repository, + ready to make changes. If not present, the `spec.ref` field from the + referenced `GitRepository` or its default will be used. + properties: + ref: + description: |- + Reference gives a branch, tag or commit to clone from the Git + repository. + properties: + branch: + description: Branch to check out, defaults to 'master' if no other field is defined. + type: string + commit: + description: |- + Commit SHA to check out, takes precedence over all reference fields. + + This can be combined with Branch to shallow clone the branch, in which + the commit is expected to exist. + type: string + name: + description: |- + Name of the reference to check out; takes precedence over Branch, Tag and SemVer. + + It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description + Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" + type: string + semver: + description: SemVer tag expression to check out, takes precedence over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + required: + - ref + type: object + commit: + description: Commit specifies how to commit to the git repository. + properties: + author: + description: |- + Author gives the email and optionally the name to use as the + author of commits. + properties: + email: + description: Email gives the email to provide when making a commit. + type: string + name: + description: Name gives the name to provide when making a commit. + type: string + required: + - email + type: object + messageTemplate: + description: |- + MessageTemplate provides a template for the commit message, + into which will be interpolated the details of the change made. + Note: The `Updated` template field has been removed. Use `Changed` instead. + type: string + messageTemplateValues: + additionalProperties: + type: string + description: |- + MessageTemplateValues provides additional values to be available to the + templating rendering. + type: object + signingKey: + description: SigningKey provides the option to sign commits with a GPG key + properties: + secretRef: + description: |- + SecretRef holds the name to a secret that contains a 'git.asc' key + corresponding to the ASCII Armored file containing the GPG signing + keypair as the value. It must be in the same namespace as the + ImageUpdateAutomation. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - secretRef + type: object + required: + - author + type: object + push: + description: |- + Push specifies how and where to push commits made by the + automation. If missing, commits are pushed (back) to + `.spec.checkout.branch` or its default. + properties: + branch: + description: |- + Branch specifies that commits should be pushed to the branch + named. The branch is created using `.spec.checkout.branch` as the + starting point, if it doesn't already exist. + type: string + options: + additionalProperties: + type: string + description: |- + Options specifies the push options that are sent to the Git + server when performing a push operation. For details, see: + https://git-scm.com/docs/git-push#Documentation/git-push.txt---push-optionltoptiongt + type: object + refspec: + description: |- + Refspec specifies the Git Refspec to use for a push operation. + If both Branch and Refspec are provided, then the commit is pushed + to the branch and also using the specified refspec. + For more details about Git Refspecs, see: + https://git-scm.com/book/en/v2/Git-Internals-The-Refspec + type: string + type: object + required: + - commit + type: object + interval: + description: |- + Interval gives an lower bound for how often the automation + run should be attempted. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + policySelector: + description: |- + PolicySelector allows to filter applied policies based on labels. + By default includes all policies in namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + sourceRef: + description: |- + SourceRef refers to the resource giving access details + to a git repository. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + default: GitRepository + description: Kind of the referent. + enum: + - GitRepository + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, defaults to the namespace of the Kubernetes resource object that contains the reference. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + Suspend tells the controller to not run this automation, until + it is unset (or set to false). Defaults to false. + type: boolean + update: + default: + strategy: Setters + description: |- + Update gives the specification for how to update the files in + the repository. This can be left empty, to use the default + value. + properties: + path: + description: |- + Path to the directory containing the manifests to be updated. + Defaults to 'None', which translates to the root path + of the GitRepositoryRef. + type: string + strategy: + default: Setters + description: Strategy names the strategy to be used. + enum: + - Setters + type: string + type: object + required: + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: ImageUpdateAutomationStatus defines the observed state of ImageUpdateAutomation + properties: + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastAutomationRunTime: + description: |- + LastAutomationRunTime records the last time the controller ran + this automation through to completion (even if no updates were + made). + format: date-time + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastPushCommit: + description: |- + LastPushCommit records the SHA1 of the last commit made by the + controller, for this automation object + type: string + lastPushTime: + description: LastPushTime records the time of the last pushed change. + format: date-time + type: string + observedGeneration: + format: int64 + type: integer + observedPolicies: + additionalProperties: + description: ImageRef represents an image reference. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + description: |- + ObservedPolicies is the list of observed ImagePolicies that were + considered by the ImageUpdateAutomation update process. + type: object + observedSourceRevision: + description: |- + ObservedPolicies []ObservedPolicy `json:"observedPolicies,omitempty"` + ObservedSourceRevision is the last observed source revision. This can be + used to determine if the source has been updated since last observation. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .status.lastAutomationRunTime + name: Last run + priority: 1 + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta2 ImageUpdateAutomation is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: ImageUpdateAutomation is the Schema for the imageupdateautomations API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation + properties: + git: + description: |- + GitSpec contains all the git-specific definitions. This is + technically optional, but in practice mandatory until there are + other kinds of source allowed. + properties: + checkout: + description: |- + Checkout gives the parameters for cloning the git repository, + ready to make changes. If not present, the `spec.ref` field from the + referenced `GitRepository` or its default will be used. + properties: + ref: + description: |- + Reference gives a branch, tag or commit to clone from the Git + repository. + properties: + branch: + description: Branch to check out, defaults to 'master' if no other field is defined. + type: string + commit: + description: |- + Commit SHA to check out, takes precedence over all reference fields. + + This can be combined with Branch to shallow clone the branch, in which + the commit is expected to exist. + type: string + name: + description: |- + Name of the reference to check out; takes precedence over Branch, Tag and SemVer. + + It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description + Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" + type: string + semver: + description: SemVer tag expression to check out, takes precedence over Tag. + type: string + tag: + description: Tag to check out, takes precedence over Branch. + type: string + type: object + required: + - ref + type: object + commit: + description: Commit specifies how to commit to the git repository. + properties: + author: + description: |- + Author gives the email and optionally the name to use as the + author of commits. + properties: + email: + description: Email gives the email to provide when making a commit. + type: string + name: + description: Name gives the name to provide when making a commit. + type: string + required: + - email + type: object + messageTemplate: + description: |- + MessageTemplate provides a template for the commit message, + into which will be interpolated the details of the change made. + Note: The `Updated` template field has been removed. Use `Changed` instead. + type: string + messageTemplateValues: + additionalProperties: + type: string + description: |- + MessageTemplateValues provides additional values to be available to the + templating rendering. + type: object + signingKey: + description: SigningKey provides the option to sign commits with a GPG key + properties: + secretRef: + description: |- + SecretRef holds the name to a secret that contains a 'git.asc' key + corresponding to the ASCII Armored file containing the GPG signing + keypair as the value. It must be in the same namespace as the + ImageUpdateAutomation. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - secretRef + type: object + required: + - author + type: object + push: + description: |- + Push specifies how and where to push commits made by the + automation. If missing, commits are pushed (back) to + `.spec.checkout.branch` or its default. + properties: + branch: + description: |- + Branch specifies that commits should be pushed to the branch + named. The branch is created using `.spec.checkout.branch` as the + starting point, if it doesn't already exist. + type: string + options: + additionalProperties: + type: string + description: |- + Options specifies the push options that are sent to the Git + server when performing a push operation. For details, see: + https://git-scm.com/docs/git-push#Documentation/git-push.txt---push-optionltoptiongt + type: object + refspec: + description: |- + Refspec specifies the Git Refspec to use for a push operation. + If both Branch and Refspec are provided, then the commit is pushed + to the branch and also using the specified refspec. + For more details about Git Refspecs, see: + https://git-scm.com/book/en/v2/Git-Internals-The-Refspec + type: string + type: object + required: + - commit + type: object + interval: + description: |- + Interval gives an lower bound for how often the automation + run should be attempted. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + policySelector: + description: |- + PolicySelector allows to filter applied policies based on labels. + By default includes all policies in namespace. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + sourceRef: + description: |- + SourceRef refers to the resource giving access details + to a git repository. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + default: GitRepository + description: Kind of the referent. + enum: + - GitRepository + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, defaults to the namespace of the Kubernetes resource object that contains the reference. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + Suspend tells the controller to not run this automation, until + it is unset (or set to false). Defaults to false. + type: boolean + update: + default: + strategy: Setters + description: |- + Update gives the specification for how to update the files in + the repository. This can be left empty, to use the default + value. + properties: + path: + description: |- + Path to the directory containing the manifests to be updated. + Defaults to 'None', which translates to the root path + of the GitRepositoryRef. + type: string + strategy: + default: Setters + description: Strategy names the strategy to be used. + enum: + - Setters + type: string + type: object + required: + - interval + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: ImageUpdateAutomationStatus defines the observed state of ImageUpdateAutomation + properties: + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastAutomationRunTime: + description: |- + LastAutomationRunTime records the last time the controller ran + this automation through to completion (even if no updates were + made). + format: date-time + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + lastPushCommit: + description: |- + LastPushCommit records the SHA1 of the last commit made by the + controller, for this automation object + type: string + lastPushTime: + description: LastPushTime records the time of the last pushed change. + format: date-time + type: string + observedGeneration: + format: int64 + type: integer + observedPolicies: + additionalProperties: + description: ImageRef represents an image reference. + properties: + digest: + description: Digest is the image's digest. + type: string + name: + description: Name is the bare image's name. + type: string + tag: + description: Tag is the image's tag. + type: string + required: + - name + - tag + type: object + description: |- + ObservedPolicies is the list of observed ImagePolicies that were + considered by the ImageUpdateAutomation update process. + type: object + observedSourceRevision: + description: |- + ObservedPolicies []ObservedPolicy `json:"observedPolicies,omitempty"` + ObservedSourceRevision is the last observed source revision. This can be + used to determine if the source has been updated since last observation. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: kustomizations.kustomize.toolkit.fluxcd.io +spec: + group: kustomize.toolkit.fluxcd.io + names: + kind: Kustomization + listKind: KustomizationList + plural: kustomizations + shortNames: + - ks + singular: kustomization + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: Kustomization is the Schema for the kustomizations API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + KustomizationSpec defines the configuration to calculate the desired state + from a Source using Kustomize. + properties: + commonMetadata: + description: |- + CommonMetadata specifies the common labels and annotations that are + applied to all resources. Any existing label or annotation will be + overridden if its key matches a common one. + properties: + annotations: + additionalProperties: + type: string + description: Annotations to be added to the object's metadata. + type: object + labels: + additionalProperties: + type: string + description: Labels to be added to the object's metadata. + type: object + type: object + components: + description: Components specifies relative paths to kustomize Components. + items: + type: string + type: array + decryption: + description: Decrypt Kubernetes secrets before applying them on the cluster. + properties: + provider: + description: Provider is the name of the decryption engine. + enum: + - sops + type: string + secretRef: + description: |- + The secret name containing the private OpenPGP keys used for decryption. + A static credential for a cloud provider defined inside the Secret + takes priority to secret-less authentication with the ServiceAccountName + field. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the service account used to + authenticate with KMS services from cloud providers. If a + static credential for a given cloud provider is defined + inside the Secret referenced by SecretRef, that static + credential takes priority. + type: string + required: + - provider + type: object + deletionPolicy: + description: |- + DeletionPolicy can be used to control garbage collection when this + Kustomization is deleted. Valid values are ('MirrorPrune', 'Delete', + 'WaitForTermination', 'Orphan'). 'MirrorPrune' mirrors the Prune field + (orphan if false, delete if true). Defaults to 'MirrorPrune'. + enum: + - MirrorPrune + - Delete + - WaitForTermination + - Orphan + type: string + dependsOn: + description: |- + DependsOn may contain a DependencyReference slice + with references to Kustomization resources that must be ready before this + Kustomization can be reconciled. + items: + description: DependencyReference defines a Kustomization dependency on another Kustomization resource. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kustomization + resource object that contains the reference. + type: string + readyExpr: + description: |- + ReadyExpr is a CEL expression that can be used to assess the readiness + of a dependency. When specified, the built-in readiness check + is replaced by the logic defined in the CEL expression. + To make the CEL expression additive to the built-in readiness check, + the feature gate `AdditiveCELDependencyCheck` must be set to `true`. + type: string + required: + - name + type: object + type: array + force: + default: false + description: |- + Force instructs the controller to recreate resources + when patching fails due to an immutable field change. + type: boolean + healthCheckExprs: + description: |- + HealthCheckExprs is a list of healthcheck expressions for evaluating the + health of custom resources using Common Expression Language (CEL). + The expressions are evaluated only when Wait or HealthChecks are specified. + items: + description: CustomHealthCheck defines the health check for custom resources. + properties: + apiVersion: + description: APIVersion of the custom resource under evaluation. + type: string + current: + description: |- + Current is the CEL expression that determines if the status + of the custom resource has reached the desired state. + type: string + failed: + description: |- + Failed is the CEL expression that determines if the status + of the custom resource has failed to reach the desired state. + type: string + inProgress: + description: |- + InProgress is the CEL expression that determines if the status + of the custom resource has not yet reached the desired state. + type: string + kind: + description: Kind of the custom resource under evaluation. + type: string + required: + - apiVersion + - current + - kind + type: object + type: array + healthChecks: + description: A list of resources to be included in the health assessment. + items: + description: |- + NamespacedObjectKindReference contains enough information to locate the typed referenced Kubernetes resource object + in any namespace. + properties: + apiVersion: + description: API version of the referent, if not specified the Kubernetes preferred version will be used. + type: string + kind: + description: Kind of the referent. + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it acts as LocalObjectReference. + type: string + required: + - kind + - name + type: object + type: array + ignoreMissingComponents: + description: |- + IgnoreMissingComponents instructs the controller to ignore Components paths + not found in source by removing them from the generated kustomization.yaml + before running kustomize build. + type: boolean + images: + description: |- + Images is a list of (image name, new name, new tag or digest) + for changing image names, tags or digests. This can also be achieved with a + patch, but this operator is simpler to specify. + items: + description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag. + properties: + digest: + description: |- + Digest is the value used to replace the original image tag. + If digest is present NewTag value is ignored. + type: string + name: + description: Name is a tag-less image name. + type: string + newName: + description: NewName is the value used to replace the original name. + type: string + newTag: + description: NewTag is the value used to replace the original tag. + type: string + required: + - name + type: object + type: array + interval: + description: |- + The interval at which to reconcile the Kustomization. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: |- + The KubeConfig for reconciling the Kustomization on a remote cluster. + When used in combination with KustomizationSpec.ServiceAccountName, + forces the controller to act on behalf of that Service Account at the + target cluster. + If the --default-service-account flag is set, its value will be used as + a controller level fallback for when KustomizationSpec.ServiceAccountName + is empty. + properties: + configMapRef: + description: |- + ConfigMapRef holds an optional name of a ConfigMap that contains + the following keys: + + - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or + `generic`. Required. + - `cluster`: the fully qualified resource name of the Kubernetes + cluster in the cloud provider API. Not used by the `generic` + provider. Required when one of `address` or `ca.crt` is not set. + - `address`: the address of the Kubernetes API server. Required + for `generic`. For the other providers, if not specified, the + first address in the cluster resource will be used, and if + specified, it must match one of the addresses in the cluster + resource. + If audiences is not set, will be used as the audience for the + `generic` provider. + - `ca.crt`: the optional PEM-encoded CA certificate for the + Kubernetes API server. If not set, the controller will use the + CA certificate from the cluster resource. + - `audiences`: the optional audiences as a list of + line-break-separated strings for the Kubernetes ServiceAccount + token. Defaults to the `address` for the `generic` provider, or + to specific values for the other providers depending on the + provider. + - `serviceAccountName`: the optional name of the Kubernetes + ServiceAccount in the same namespace that should be used + for authentication. If not specified, the controller + ServiceAccount will be used. + + Mutually exclusive with SecretRef. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef holds an optional name of a secret that contains a key with + the kubeconfig file as the value. If no key is set, the key will default + to 'value'. Mutually exclusive with ConfigMapRef. + It is recommended that the kubeconfig is self-contained, and the secret + is regularly updated if credentials such as a cloud-access-token expire. + Cloud specific `cmd-path` auth helpers will not function without adding + binaries and credentials to the Pod that is responsible for reconciling + Kubernetes resources. Supported only for the generic provider. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + x-kubernetes-validations: + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified + rule: has(self.configMapRef) || has(self.secretRef) + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified + rule: '!has(self.configMapRef) || !has(self.secretRef)' + namePrefix: + description: NamePrefix will prefix the names of all managed resources. + maxLength: 200 + minLength: 1 + type: string + nameSuffix: + description: NameSuffix will suffix the names of all managed resources. + maxLength: 200 + minLength: 1 + type: string + patches: + description: |- + Strategic merge and JSON patches, defined as inline YAML objects, + capable of targeting objects based on kind, label and annotation selectors. + items: + description: |- + Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should + be applied to. + properties: + patch: + description: |- + Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with + an array of operation objects. + type: string + target: + description: Target points to the resources that the patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + type: object + type: array + path: + description: |- + Path to the directory containing the kustomization.yaml file, or the + set of plain YAMLs a kustomization.yaml should be generated for. + Defaults to 'None', which translates to the root path of the SourceRef. + type: string + postBuild: + description: |- + PostBuild describes which actions to perform on the YAML manifest + generated by building the kustomize overlay. + properties: + substitute: + additionalProperties: + type: string + description: |- + Substitute holds a map of key/value pairs. + The variables defined in your YAML manifests that match any of the keys + defined in the map will be substituted with the set value. + Includes support for bash string replacement functions + e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}. + type: object + substituteFrom: + description: |- + SubstituteFrom holds references to ConfigMaps and Secrets containing + the variables and their values to be substituted in the YAML manifests. + The ConfigMap and the Secret data keys represent the var names, and they + must match the vars declared in the manifests for the substitution to + happen. + items: + description: |- + SubstituteReference contains a reference to a resource containing + the variables name and value. + properties: + kind: + description: Kind of the values referent, valid values are ('Secret', 'ConfigMap'). + enum: + - Secret + - ConfigMap + type: string + name: + description: |- + Name of the values referent. Should reside in the same namespace as the + referring resource. + maxLength: 253 + minLength: 1 + type: string + optional: + default: false + description: |- + Optional indicates whether the referenced resource must exist, or whether to + tolerate its absence. If true and the referenced resource is absent, proceed + as if the resource was present but empty, without any variables defined. + type: boolean + required: + - kind + - name + type: object + type: array + type: object + prune: + description: Prune enables garbage collection. + type: boolean + retryInterval: + description: |- + The interval at which to retry a previously failed reconciliation. + When not specified, the controller uses the KustomizationSpec.Interval + value to retry failures. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + serviceAccountName: + description: |- + The name of the Kubernetes service account to impersonate + when reconciling this Kustomization. + type: string + sourceRef: + description: Reference of the source where the kustomization file is. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - GitRepository + - Bucket + - ExternalArtifact + type: string + name: + description: Name of the referent. + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kubernetes + resource object that contains the reference. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + This flag tells the controller to suspend subsequent kustomize executions, + it does not apply to already started executions. Defaults to false. + type: boolean + targetNamespace: + description: |- + TargetNamespace sets or overrides the namespace in the + kustomization.yaml file. + maxLength: 63 + minLength: 1 + type: string + timeout: + description: |- + Timeout for validation, apply and health checking operations. + Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + wait: + description: |- + Wait instructs the controller to check the health of all the reconciled + resources. When enabled, the HealthChecks are ignored. Defaults to false. + type: boolean + required: + - interval + - prune + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: KustomizationStatus defines the observed state of a kustomization. + properties: + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + history: + description: |- + History contains a set of snapshots of the last reconciliation attempts + tracking the revision, the state and the duration of each attempt. + items: + description: |- + Snapshot represents a point-in-time record of a group of resources reconciliation, + including timing information, status, and a unique digest identifier. + properties: + digest: + description: Digest is the checksum in the format `:` of the resources in this snapshot. + type: string + firstReconciled: + description: FirstReconciled is the time when this revision was first reconciled to the cluster. + format: date-time + type: string + lastReconciled: + description: LastReconciled is the time when this revision was last reconciled to the cluster. + format: date-time + type: string + lastReconciledDuration: + description: LastReconciledDuration is time it took to reconcile the resources in this revision. + type: string + lastReconciledStatus: + description: LastReconciledStatus is the status of the last reconciliation. + type: string + metadata: + additionalProperties: + type: string + description: Metadata contains additional information about the snapshot. + type: object + totalReconciliations: + description: TotalReconciliations is the total number of reconciliations that have occurred for this snapshot. + format: int64 + type: integer + required: + - digest + - firstReconciled + - lastReconciled + - lastReconciledDuration + - lastReconciledStatus + - totalReconciliations + type: object + type: array + inventory: + description: |- + Inventory contains the list of Kubernetes resource object references that + have been successfully applied. + properties: + entries: + description: Entries of Kubernetes resource object references. + items: + description: ResourceRef contains the information necessary to locate a resource within a cluster. + properties: + id: + description: |- + ID is the string representation of the Kubernetes resource object's metadata, + in the format '___'. + type: string + v: + description: Version is the API version of the Kubernetes resource object's kind. + type: string + required: + - id + - v + type: object + type: array + required: + - entries + type: object + lastAppliedOriginRevision: + description: |- + The last successfully applied origin revision. + Equals the origin revision of the applied Artifact from the referenced Source. + Usually present on the Metadata of the applied Artifact and depends on the + Source type, e.g. for OCI it's the value associated with the key + "org.opencontainers.image.revision". + type: string + lastAppliedRevision: + description: |- + The last successfully applied revision. + Equals the Revision of the applied Artifact from the referenced Source. + type: string + lastAttemptedRevision: + description: LastAttemptedRevision is the revision of the last reconciliation attempt. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 Kustomization is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: Kustomization is the Schema for the kustomizations API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: KustomizationSpec defines the configuration to calculate the desired state from a Source using Kustomize. + properties: + commonMetadata: + description: |- + CommonMetadata specifies the common labels and annotations that are applied to all resources. + Any existing label or annotation will be overridden if its key matches a common one. + properties: + annotations: + additionalProperties: + type: string + description: Annotations to be added to the object's metadata. + type: object + labels: + additionalProperties: + type: string + description: Labels to be added to the object's metadata. + type: object + type: object + components: + description: Components specifies relative paths to specifications of other Components. + items: + type: string + type: array + decryption: + description: Decrypt Kubernetes secrets before applying them on the cluster. + properties: + provider: + description: Provider is the name of the decryption engine. + enum: + - sops + type: string + secretRef: + description: The secret name containing the private OpenPGP keys used for decryption. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + dependsOn: + description: |- + DependsOn may contain a meta.NamespacedObjectReference slice + with references to Kustomization resources that must be ready before this + Kustomization can be reconciled. + items: + description: |- + NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any + namespace. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it acts as LocalObjectReference. + type: string + required: + - name + type: object + type: array + force: + default: false + description: |- + Force instructs the controller to recreate resources + when patching fails due to an immutable field change. + type: boolean + healthChecks: + description: A list of resources to be included in the health assessment. + items: + description: |- + NamespacedObjectKindReference contains enough information to locate the typed referenced Kubernetes resource object + in any namespace. + properties: + apiVersion: + description: API version of the referent, if not specified the Kubernetes preferred version will be used. + type: string + kind: + description: Kind of the referent. + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, when not specified it acts as LocalObjectReference. + type: string + required: + - kind + - name + type: object + type: array + images: + description: |- + Images is a list of (image name, new name, new tag or digest) + for changing image names, tags or digests. This can also be achieved with a + patch, but this operator is simpler to specify. + items: + description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag. + properties: + digest: + description: |- + Digest is the value used to replace the original image tag. + If digest is present NewTag value is ignored. + type: string + name: + description: Name is a tag-less image name. + type: string + newName: + description: NewName is the value used to replace the original name. + type: string + newTag: + description: NewTag is the value used to replace the original tag. + type: string + required: + - name + type: object + type: array + interval: + description: The interval at which to reconcile the Kustomization. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + kubeConfig: + description: |- + The KubeConfig for reconciling the Kustomization on a remote cluster. + When used in combination with KustomizationSpec.ServiceAccountName, + forces the controller to act on behalf of that Service Account at the + target cluster. + If the --default-service-account flag is set, its value will be used as + a controller level fallback for when KustomizationSpec.ServiceAccountName + is empty. + properties: + configMapRef: + description: |- + ConfigMapRef holds an optional name of a ConfigMap that contains + the following keys: + + - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or + `generic`. Required. + - `cluster`: the fully qualified resource name of the Kubernetes + cluster in the cloud provider API. Not used by the `generic` + provider. Required when one of `address` or `ca.crt` is not set. + - `address`: the address of the Kubernetes API server. Required + for `generic`. For the other providers, if not specified, the + first address in the cluster resource will be used, and if + specified, it must match one of the addresses in the cluster + resource. + If audiences is not set, will be used as the audience for the + `generic` provider. + - `ca.crt`: the optional PEM-encoded CA certificate for the + Kubernetes API server. If not set, the controller will use the + CA certificate from the cluster resource. + - `audiences`: the optional audiences as a list of + line-break-separated strings for the Kubernetes ServiceAccount + token. Defaults to the `address` for the `generic` provider, or + to specific values for the other providers depending on the + provider. + - `serviceAccountName`: the optional name of the Kubernetes + ServiceAccount in the same namespace that should be used + for authentication. If not specified, the controller + ServiceAccount will be used. + + Mutually exclusive with SecretRef. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef holds an optional name of a secret that contains a key with + the kubeconfig file as the value. If no key is set, the key will default + to 'value'. Mutually exclusive with ConfigMapRef. + It is recommended that the kubeconfig is self-contained, and the secret + is regularly updated if credentials such as a cloud-access-token expire. + Cloud specific `cmd-path` auth helpers will not function without adding + binaries and credentials to the Pod that is responsible for reconciling + Kubernetes resources. Supported only for the generic provider. + properties: + key: + description: Key in the Secret, when not specified an implementation-specific default key is used. + type: string + name: + description: Name of the Secret. + type: string + required: + - name + type: object + type: object + x-kubernetes-validations: + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified + rule: has(self.configMapRef) || has(self.secretRef) + - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified + rule: '!has(self.configMapRef) || !has(self.secretRef)' + patches: + description: |- + Strategic merge and JSON patches, defined as inline YAML objects, + capable of targeting objects based on kind, label and annotation selectors. + items: + description: |- + Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should + be applied to. + properties: + patch: + description: |- + Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with + an array of operation objects. + type: string + target: + description: Target points to the resources that the patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + type: object + type: array + patchesJson6902: + description: |- + JSON 6902 patches, defined as inline YAML objects. + Deprecated: Use Patches instead. + items: + description: JSON6902Patch contains a JSON6902 patch and the target the patch should be applied to. + properties: + patch: + description: Patch contains the JSON6902 patch document with an array of operation objects. + items: + description: |- + JSON6902 is a JSON6902 operation object. + https://datatracker.ietf.org/doc/html/rfc6902#section-4 + properties: + from: + description: |- + From contains a JSON-pointer value that references a location within the target document where the operation is + performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations. + type: string + op: + description: |- + Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or + "test". + https://datatracker.ietf.org/doc/html/rfc6902#section-4 + enum: + - test + - remove + - add + - replace + - move + - copy + type: string + path: + description: |- + Path contains the JSON-pointer value that references a location within the target document where the operation + is performed. The meaning of the value depends on the value of Op. + type: string + value: + description: |- + Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into + account by all operations. + x-kubernetes-preserve-unknown-fields: true + required: + - op + - path + type: object + type: array + target: + description: Target points to the resources that the patch document should be applied to. + properties: + annotationSelector: + description: |- + AnnotationSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource annotations. + type: string + group: + description: |- + Group is the API group to select resources from. + Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + kind: + description: |- + Kind of the API Group to select resources from. + Together with Group and Version it is capable of unambiguously + identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + labelSelector: + description: |- + LabelSelector is a string that follows the label selection expression + https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api + It matches with the resource labels. + type: string + name: + description: Name to match resources with. + type: string + namespace: + description: Namespace to select resources from. + type: string + version: + description: |- + Version of the API Group to select resources from. + Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. + https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md + type: string + type: object + required: + - patch + - target + type: object + type: array + patchesStrategicMerge: + description: |- + Strategic merge patches, defined as inline YAML objects. + Deprecated: Use Patches instead. + items: + x-kubernetes-preserve-unknown-fields: true + type: array + path: + description: |- + Path to the directory containing the kustomization.yaml file, or the + set of plain YAMLs a kustomization.yaml should be generated for. + Defaults to 'None', which translates to the root path of the SourceRef. + type: string + postBuild: + description: |- + PostBuild describes which actions to perform on the YAML manifest + generated by building the kustomize overlay. + properties: + substitute: + additionalProperties: + type: string + description: |- + Substitute holds a map of key/value pairs. + The variables defined in your YAML manifests + that match any of the keys defined in the map + will be substituted with the set value. + Includes support for bash string replacement functions + e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}. + type: object + substituteFrom: + description: |- + SubstituteFrom holds references to ConfigMaps and Secrets containing + the variables and their values to be substituted in the YAML manifests. + The ConfigMap and the Secret data keys represent the var names and they + must match the vars declared in the manifests for the substitution to happen. + items: + description: |- + SubstituteReference contains a reference to a resource containing + the variables name and value. + properties: + kind: + description: Kind of the values referent, valid values are ('Secret', 'ConfigMap'). + enum: + - Secret + - ConfigMap + type: string + name: + description: |- + Name of the values referent. Should reside in the same namespace as the + referring resource. + maxLength: 253 + minLength: 1 + type: string + optional: + default: false + description: |- + Optional indicates whether the referenced resource must exist, or whether to + tolerate its absence. If true and the referenced resource is absent, proceed + as if the resource was present but empty, without any variables defined. + type: boolean + required: + - kind + - name + type: object + type: array + type: object + prune: + description: Prune enables garbage collection. + type: boolean + retryInterval: + description: |- + The interval at which to retry a previously failed reconciliation. + When not specified, the controller uses the KustomizationSpec.Interval + value to retry failures. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + serviceAccountName: + description: |- + The name of the Kubernetes service account to impersonate + when reconciling this Kustomization. + type: string + sourceRef: + description: Reference of the source where the kustomization file is. + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - GitRepository + - Bucket + type: string + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent, defaults to the namespace of the Kubernetes resource object that contains the reference. + type: string + required: + - kind + - name + type: object + suspend: + description: |- + This flag tells the controller to suspend subsequent kustomize executions, + it does not apply to already started executions. Defaults to false. + type: boolean + targetNamespace: + description: |- + TargetNamespace sets or overrides the namespace in the + kustomization.yaml file. + maxLength: 63 + minLength: 1 + type: string + timeout: + description: |- + Timeout for validation, apply and health checking operations. + Defaults to 'Interval' duration. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + validation: + description: 'Deprecated: Not used in v1beta2.' + enum: + - none + - client + - server + type: string + wait: + description: |- + Wait instructs the controller to check the health of all the reconciled resources. + When enabled, the HealthChecks are ignored. Defaults to false. + type: boolean + required: + - interval + - prune + - sourceRef + type: object + status: + default: + observedGeneration: -1 + description: KustomizationStatus defines the observed state of a kustomization. + properties: + conditions: + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + inventory: + description: Inventory contains the list of Kubernetes resource object references that have been successfully applied. + properties: + entries: + description: Entries of Kubernetes resource object references. + items: + description: ResourceRef contains the information necessary to locate a resource within a cluster. + properties: + id: + description: |- + ID is the string representation of the Kubernetes resource object's metadata, + in the format '___'. + type: string + v: + description: Version is the API version of the Kubernetes resource object's kind. + type: string + required: + - id + - v + type: object + type: array + required: + - entries + type: object + lastAppliedRevision: + description: |- + The last successfully applied revision. + Equals the Revision of the applied Artifact from the referenced Source. + type: string + lastAttemptedRevision: + description: LastAttemptedRevision is the revision of the last reconciliation attempt. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: ocirepositories.source.toolkit.fluxcd.io +spec: + group: source.toolkit.fluxcd.io + names: + kind: OCIRepository + listKind: OCIRepositoryList + plural: ocirepositories + shortNames: + - ocirepo + singular: ocirepository + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: OCIRepository is the Schema for the ocirepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: OCIRepositorySpec defines the desired state of OCIRepository + properties: + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container registry. + type: boolean + interval: + description: |- + Interval at which the OCIRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + layerSelector: + description: |- + LayerSelector specifies which layer should be extracted from the OCI artifact. + When not specified, the first layer found in the artifact is selected. + properties: + mediaType: + description: |- + MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The + first layer matching this type is selected. + type: string + operation: + description: |- + Operation specifies how the selected layer should be processed. + By default, the layer compressed content is extracted to storage. + When the operation is set to 'copy', the layer compressed content + is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object + provider: + default: generic + description: |- + The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the container registry. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + ref: + description: |- + The OCI reference to pull and monitor for changes, + defaults to the latest tag. + properties: + digest: + description: |- + Digest is the image digest to pull, takes precedence over SemVer. + The value should be in the format 'sha256:'. + type: string + semver: + description: |- + SemVer is the range of tags to pull selecting the latest within + the range, takes precedence over Tag. + type: string + semverFilter: + description: SemverFilter is a regex pattern to filter the tags within the SemVer range. + type: string + tag: + description: Tag is the image tag to pull, defaults to latest. + type: string + type: object + secretRef: + description: |- + SecretRef contains the secret name containing the registry login + credentials to resolve image metadata. + The secret must be of type kubernetes.io/dockerconfigjson. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the image pull if the service account has attached pull secrets. For more information: + https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account + type: string + suspend: + description: This flag tells the controller to suspend the reconciliation of this source. + type: boolean + timeout: + default: 60s + description: The timeout for remote OCI Repository operations like pulling, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + url: + description: |- + URL is a reference to an OCI artifact repository hosted + on a remote container registry. + pattern: ^oci://.*$ + type: string + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + properties: + matchOIDCIdentity: + description: |- + MatchOIDCIdentity specifies the identity matching criteria to use + while verifying an OCI artifact which was signed using Cosign keyless + signing. The artifact's identity is deemed to be verified if any of the + specified matchers match against the identity. + items: + description: |- + OIDCIdentityMatch specifies options for verifying the certificate identity, + i.e. the issuer and the subject of the certificate. + properties: + issuer: + description: |- + Issuer specifies the regex pattern to match against to verify + the OIDC issuer in the Fulcio certificate. The pattern must be a + valid Go regular expression. + type: string + subject: + description: |- + Subject specifies the regex pattern to match against to verify + the identity subject in the Fulcio certificate. The pattern must + be a valid Go regular expression. + type: string + required: + - issuer + - subject + type: object + type: array + provider: + default: cosign + description: Provider specifies the technology used to sign the OCI Artifact. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: OCIRepositoryStatus defines the observed state of OCIRepository + properties: + artifact: + description: Artifact represents the output of the last successful OCI Repository sync. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the OCIRepository. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + observedLayerSelector: + description: |- + ObservedLayerSelector is the observed layer selector used for constructing + the source artifact. + properties: + mediaType: + description: |- + MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The + first layer matching this type is selected. + type: string + operation: + description: |- + Operation specifies how the selected layer should be processed. + By default, the layer compressed content is extracted to storage. + When the operation is set to 'copy', the layer compressed content + is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object + url: + description: URL is the download link for the artifact output of the last OCI Repository sync. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.url + name: URL + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: v1beta2 OCIRepository is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: OCIRepository is the Schema for the ocirepositories API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: OCIRepositorySpec defines the desired state of OCIRepository + properties: + certSecretRef: + description: |- + CertSecretRef can be given the name of a Secret containing + either or both of + + - a PEM-encoded client certificate (`tls.crt`) and private + key (`tls.key`); + - a PEM-encoded CA certificate (`ca.crt`) + + and whichever are supplied, will be used for connecting to the + registry. The client cert and key are useful if you are + authenticating with a certificate; the CA cert is useful if + you are using a self-signed server certificate. The Secret must + be of type `Opaque` or `kubernetes.io/tls`. + + Note: Support for the `caFile`, `certFile` and `keyFile` keys have + been deprecated. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + ignore: + description: |- + Ignore overrides the set of excluded patterns in the .sourceignore format + (which is the same as .gitignore). If not provided, a default will be used, + consult the documentation for your version to find out what those are. + type: string + insecure: + description: Insecure allows connecting to a non-TLS HTTP container registry. + type: boolean + interval: + description: |- + Interval at which the OCIRepository URL is checked for updates. + This interval is approximate and may be subject to jitter to ensure + efficient use of resources. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + layerSelector: + description: |- + LayerSelector specifies which layer should be extracted from the OCI artifact. + When not specified, the first layer found in the artifact is selected. + properties: + mediaType: + description: |- + MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The + first layer matching this type is selected. + type: string + operation: + description: |- + Operation specifies how the selected layer should be processed. + By default, the layer compressed content is extracted to storage. + When the operation is set to 'copy', the layer compressed content + is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object + provider: + default: generic + description: |- + The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. + When not specified, defaults to 'generic'. + enum: + - generic + - aws + - azure + - gcp + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + to use while communicating with the container registry. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + ref: + description: |- + The OCI reference to pull and monitor for changes, + defaults to the latest tag. + properties: + digest: + description: |- + Digest is the image digest to pull, takes precedence over SemVer. + The value should be in the format 'sha256:'. + type: string + semver: + description: |- + SemVer is the range of tags to pull selecting the latest within + the range, takes precedence over Tag. + type: string + semverFilter: + description: SemverFilter is a regex pattern to filter the tags within the SemVer range. + type: string + tag: + description: Tag is the image tag to pull, defaults to latest. + type: string + type: object + secretRef: + description: |- + SecretRef contains the secret name containing the registry login + credentials to resolve image metadata. + The secret must be of type kubernetes.io/dockerconfigjson. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate + the image pull if the service account has attached pull secrets. For more information: + https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account + type: string + suspend: + description: This flag tells the controller to suspend the reconciliation of this source. + type: boolean + timeout: + default: 60s + description: The timeout for remote OCI Repository operations like pulling, defaults to 60s. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + url: + description: |- + URL is a reference to an OCI artifact repository hosted + on a remote container registry. + pattern: ^oci://.*$ + type: string + verify: + description: |- + Verify contains the secret name containing the trusted public keys + used to verify the signature and specifies which provider to use to check + whether OCI image is authentic. + properties: + matchOIDCIdentity: + description: |- + MatchOIDCIdentity specifies the identity matching criteria to use + while verifying an OCI artifact which was signed using Cosign keyless + signing. The artifact's identity is deemed to be verified if any of the + specified matchers match against the identity. + items: + description: |- + OIDCIdentityMatch specifies options for verifying the certificate identity, + i.e. the issuer and the subject of the certificate. + properties: + issuer: + description: |- + Issuer specifies the regex pattern to match against to verify + the OIDC issuer in the Fulcio certificate. The pattern must be a + valid Go regular expression. + type: string + subject: + description: |- + Subject specifies the regex pattern to match against to verify + the identity subject in the Fulcio certificate. The pattern must + be a valid Go regular expression. + type: string + required: + - issuer + - subject + type: object + type: array + provider: + default: cosign + description: Provider specifies the technology used to sign the OCI Artifact. + enum: + - cosign + - notation + type: string + secretRef: + description: |- + SecretRef specifies the Kubernetes Secret containing the + trusted public keys. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + required: + - provider + type: object + required: + - interval + - url + type: object + status: + default: + observedGeneration: -1 + description: OCIRepositoryStatus defines the observed state of OCIRepository + properties: + artifact: + description: Artifact represents the output of the last successful OCI Repository sync. + properties: + digest: + description: Digest is the digest of the file in the form of ':'. + pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ + type: string + lastUpdateTime: + description: |- + LastUpdateTime is the timestamp corresponding to the last update of the + Artifact. + format: date-time + type: string + metadata: + additionalProperties: + type: string + description: Metadata holds upstream information such as OCI annotations. + type: object + path: + description: |- + Path is the relative file path of the Artifact. It can be used to locate + the file in the root of the Artifact storage on the local file system of + the controller managing the Source. + type: string + revision: + description: |- + Revision is a human-readable identifier traceable in the origin source + system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. + type: string + size: + description: Size is the number of bytes in the file. + format: int64 + type: integer + url: + description: |- + URL is the HTTP address of the Artifact as exposed by the controller + managing the Source. It can be used to retrieve the Artifact for + consumption, e.g. by another controller applying the Artifact contents. + type: string + required: + - digest + - lastUpdateTime + - path + - revision + - url + type: object + conditions: + description: Conditions holds the conditions for the OCIRepository. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + contentConfigChecksum: + description: |- + ContentConfigChecksum is a checksum of all the configurations related to + the content of the source artifact: + - .spec.ignore + - .spec.layerSelector + observed in .status.observedGeneration version of the object. This can + be used to determine if the content configuration has changed and the + artifact needs to be rebuilt. + It has the format of `:`, for example: `sha256:`. + + Deprecated: Replaced with explicit fields for observed artifact content + config in the status. + type: string + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation. + format: int64 + type: integer + observedIgnore: + description: |- + ObservedIgnore is the observed exclusion patterns used for constructing + the source artifact. + type: string + observedLayerSelector: + description: |- + ObservedLayerSelector is the observed layer selector used for constructing + the source artifact. + properties: + mediaType: + description: |- + MediaType specifies the OCI media type of the layer + which should be extracted from the OCI Artifact. The + first layer matching this type is selected. + type: string + operation: + description: |- + Operation specifies how the selected layer should be processed. + By default, the layer compressed content is extracted to storage. + When the operation is set to 'copy', the layer compressed content + is persisted to storage as it is. + enum: + - extract + - copy + type: string + type: object + url: + description: URL is the download link for the artifact output of the last OCI Repository sync. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: providers.notification.toolkit.fluxcd.io +spec: + group: notification.toolkit.fluxcd.io + names: + kind: Provider + listKind: ProviderList + plural: providers + singular: provider + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 Provider is deprecated, upgrade to v1beta3 + name: v1beta2 + schema: + openAPIV3Schema: + description: Provider is the Schema for the providers API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ProviderSpec defines the desired state of the Provider. + properties: + address: + description: |- + Address specifies the endpoint, in a generic sense, to where alerts are sent. + What kind of endpoint depends on the specific Provider type being used. + For the generic Provider, for example, this is an HTTP/S address. + For other Provider types this could be a project ID or a namespace. + maxLength: 2048 + type: string + certSecretRef: + description: |- + CertSecretRef specifies the Secret containing + a PEM-encoded CA certificate (in the `ca.crt` key). + + Note: Support for the `caFile` key has + been deprecated. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + channel: + description: Channel specifies the destination channel where events should be posted. + maxLength: 2048 + type: string + interval: + description: Interval at which to reconcile the Provider with its Secret references. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + proxy: + description: Proxy the HTTP/S address of the proxy server. + maxLength: 2048 + pattern: ^(http|https)://.*$ + type: string + secretRef: + description: |- + SecretRef specifies the Secret containing the authentication + credentials for this Provider. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend subsequent + events handling for this Provider. + type: boolean + timeout: + description: Timeout for sending alerts to the Provider. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + type: + description: Type specifies which Provider implementation to use. + enum: + - slack + - discord + - msteams + - rocket + - generic + - generic-hmac + - github + - gitlab + - gitea + - bitbucketserver + - bitbucket + - azuredevops + - googlechat + - googlepubsub + - webex + - sentry + - azureeventhub + - telegram + - lark + - matrix + - opsgenie + - alertmanager + - grafana + - githubdispatch + - pagerduty + - datadog + type: string + username: + description: Username specifies the name under which events are posted. + maxLength: 2048 + type: string + required: + - type + type: object + status: + default: + observedGeneration: -1 + description: ProviderStatus defines the observed state of the Provider. + properties: + conditions: + description: Conditions holds the conditions for the Provider. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta3 + schema: + openAPIV3Schema: + description: Provider is the Schema for the providers API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ProviderSpec defines the desired state of the Provider. + properties: + address: + description: |- + Address specifies the endpoint, in a generic sense, to where alerts are sent. + What kind of endpoint depends on the specific Provider type being used. + For the generic Provider, for example, this is an HTTP/S address. + For other Provider types this could be a project ID or a namespace. + maxLength: 2048 + type: string + certSecretRef: + description: |- + CertSecretRef specifies the Secret containing TLS certificates + for secure communication. + + Supported configurations: + - CA-only: Server authentication (provide ca.crt only) + - mTLS: Mutual authentication (provide ca.crt + tls.crt + tls.key) + - Client-only: Client authentication with system CA (provide tls.crt + tls.key only) + + Legacy keys "caFile", "certFile", "keyFile" are supported but deprecated. Use "ca.crt", "tls.crt", "tls.key" instead. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + channel: + description: Channel specifies the destination channel where events should be posted. + maxLength: 2048 + type: string + commitStatusExpr: + description: |- + CommitStatusExpr is a CEL expression that evaluates to a string value + that can be used to generate a custom commit status message for use + with eligible Provider types (github, gitlab, gitea, bitbucketserver, + bitbucket, azuredevops). Supported variables are: event, provider, + and alert. + type: string + interval: + description: |- + Interval at which to reconcile the Provider with its Secret references. + Deprecated and not used in v1beta3. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + proxy: + description: |- + Proxy the HTTP/S address of the proxy server. + Deprecated: Use ProxySecretRef instead. Will be removed in v1. + maxLength: 2048 + pattern: ^(http|https)://.*$ + type: string + proxySecretRef: + description: |- + ProxySecretRef specifies the Secret containing the proxy configuration + for this Provider. The Secret should contain an 'address' key with the + HTTP/S address of the proxy server. Optional 'username' and 'password' + keys can be provided for proxy authentication. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + secretRef: + description: |- + SecretRef specifies the Secret containing the authentication + credentials for this Provider. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount used to + authenticate with cloud provider services through workload identity. + This enables multi-tenant authentication without storing static credentials. + + Supported provider types: azureeventhub, azuredevops, googlepubsub + + When specified, the controller will: + 1. Create an OIDC token for the specified ServiceAccount + 2. Exchange it for cloud provider credentials via STS + 3. Use the obtained credentials for API authentication + + When unspecified, controller-level authentication is used (single-tenant). + + An error is thrown if static credentials are also defined in SecretRef. + This field requires the ObjectLevelWorkloadIdentity feature gate to be enabled. + type: string + suspend: + description: |- + Suspend tells the controller to suspend subsequent + events handling for this Provider. + type: boolean + timeout: + description: Timeout for sending alerts to the Provider. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ + type: string + type: + description: Type specifies which Provider implementation to use. + enum: + - slack + - discord + - msteams + - rocket + - generic + - generic-hmac + - github + - gitlab + - gitea + - bitbucketserver + - bitbucket + - azuredevops + - googlechat + - googlepubsub + - webex + - sentry + - azureeventhub + - telegram + - lark + - matrix + - opsgenie + - alertmanager + - grafana + - githubdispatch + - pagerduty + - datadog + - nats + - zulip + - otel + type: string + username: + description: Username specifies the name under which events are posted. + maxLength: 2048 + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: spec.commitStatusExpr is only supported for the 'github', 'gitlab', 'gitea', 'bitbucketserver', 'bitbucket', 'azuredevops' provider types + rule: self.type == 'github' || self.type == 'gitlab' || self.type == 'gitea' || self.type == 'bitbucketserver' || self.type == 'bitbucket' || self.type == 'azuredevops' || !has(self.commitStatusExpr) + type: object + served: true + storage: true + subresources: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + controller-gen.kubebuilder.io/version: v0.19.0 + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: receivers.notification.toolkit.fluxcd.io +spec: + group: notification.toolkit.fluxcd.io + names: + kind: Receiver + listKind: ReceiverList + plural: receivers + singular: receiver + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: Receiver is the Schema for the receivers API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ReceiverSpec defines the desired state of the Receiver. + properties: + events: + description: |- + Events specifies the list of event types to handle, + e.g. 'push' for GitHub or 'Push Hook' for GitLab. + items: + type: string + type: array + interval: + default: 10m + description: Interval at which to reconcile the Receiver with its Secret references. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + resourceFilter: + description: |- + ResourceFilter is a CEL expression expected to return a boolean that is + evaluated for each resource referenced in the Resources field when a + webhook is received. If the expression returns false then the controller + will not request a reconciliation for the resource. + When the expression is specified the controller will parse it and mark + the object as terminally failed if the expression is invalid or does not + return a boolean. + type: string + resources: + description: A list of resources to be notified about changes. + items: + description: |- + CrossNamespaceObjectReference contains enough information to let you locate the + typed referenced object at cluster level + properties: + apiVersion: + description: API version of the referent + type: string + kind: + description: Kind of the referent + enum: + - Bucket + - GitRepository + - Kustomization + - HelmRelease + - HelmChart + - HelmRepository + - ImageRepository + - ImagePolicy + - ImageUpdateAutomation + - OCIRepository + type: string + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + MatchLabels requires the name to be set to `*`. + type: object + name: + description: |- + Name of the referent + If multiple resources are targeted `*` may be set. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent + maxLength: 253 + minLength: 1 + type: string + required: + - kind + - name + type: object + type: array + secretRef: + description: |- + SecretRef specifies the Secret containing the token used + to validate the payload authenticity. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend subsequent + events handling for this receiver. + type: boolean + type: + description: |- + Type of webhook sender, used to determine + the validation procedure and payload deserialization. + enum: + - generic + - generic-hmac + - github + - gitlab + - bitbucket + - harbor + - dockerhub + - quay + - gcr + - nexus + - acr + - cdevents + type: string + required: + - resources + - secretRef + - type + type: object + status: + default: + observedGeneration: -1 + description: ReceiverStatus defines the observed state of the Receiver. + properties: + conditions: + description: Conditions holds the conditions for the Receiver. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of the Receiver object. + format: int64 + type: integer + webhookPath: + description: |- + WebhookPath is the generated incoming webhook address in the format + of '/hook/sha256sum(token+name+namespace)'. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.conditions[?(@.type=="Ready")].status + name: Ready + type: string + - jsonPath: .status.conditions[?(@.type=="Ready")].message + name: Status + type: string + deprecated: true + deprecationWarning: v1beta2 Receiver is deprecated, upgrade to v1 + name: v1beta2 + schema: + openAPIV3Schema: + description: Receiver is the Schema for the receivers API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ReceiverSpec defines the desired state of the Receiver. + properties: + events: + description: |- + Events specifies the list of event types to handle, + e.g. 'push' for GitHub or 'Push Hook' for GitLab. + items: + type: string + type: array + interval: + description: Interval at which to reconcile the Receiver with its Secret references. + pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ + type: string + resources: + description: A list of resources to be notified about changes. + items: + description: |- + CrossNamespaceObjectReference contains enough information to let you locate the + typed referenced object at cluster level + properties: + apiVersion: + description: API version of the referent + type: string + kind: + description: Kind of the referent + enum: + - Bucket + - GitRepository + - Kustomization + - HelmRelease + - HelmChart + - HelmRepository + - ImageRepository + - ImagePolicy + - ImageUpdateAutomation + - OCIRepository + type: string + matchLabels: + additionalProperties: + type: string + description: |- + MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + MatchLabels requires the name to be set to `*`. + type: object + name: + description: |- + Name of the referent + If multiple resources are targeted `*` may be set. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace of the referent + maxLength: 253 + minLength: 1 + type: string + required: + - kind + - name + type: object + type: array + secretRef: + description: |- + SecretRef specifies the Secret containing the token used + to validate the payload authenticity. + properties: + name: + description: Name of the referent. + type: string + required: + - name + type: object + suspend: + description: |- + Suspend tells the controller to suspend subsequent + events handling for this receiver. + type: boolean + type: + description: |- + Type of webhook sender, used to determine + the validation procedure and payload deserialization. + enum: + - generic + - generic-hmac + - github + - gitlab + - bitbucket + - harbor + - dockerhub + - quay + - gcr + - nexus + - acr + type: string + required: + - resources + - secretRef + - type + type: object + status: + default: + observedGeneration: -1 + description: ReceiverStatus defines the observed state of the Receiver. + properties: + conditions: + description: Conditions holds the conditions for the Receiver. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + lastHandledReconcileAt: + description: |- + LastHandledReconcileAt holds the value of the most recent + reconcile request value, so a change of the annotation value + can be detected. + type: string + observedGeneration: + description: ObservedGeneration is the last observed generation of the Receiver object. + format: int64 + type: integer + url: + description: |- + URL is the generated incoming webhook address in the format + of '/hook/sha256sum(token+name+namespace)'. + Deprecated: Replaced by WebhookPath. + type: string + webhookPath: + description: |- + WebhookPath is the generated incoming webhook address in the format + of '/hook/sha256sum(token+name+namespace)'. + type: string + type: object + type: object + served: true + storage: false + subresources: + status: {} +--- +apiVersion: v1 +kind: Namespace +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + pod-security.kubernetes.io/enforce: privileged + name: cozy-fluxcd +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: flux + namespace: cozy-fluxcd +spec: + hard: + pods: "1000" + scopeSelector: + matchExpressions: + - operator: In + scopeName: PriorityClass + values: + - system-node-critical + - system-cluster-critical +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: flux + namespace: cozy-fluxcd +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + rbac.authorization.k8s.io/aggregate-to-admin: "true" + rbac.authorization.k8s.io/aggregate-to-edit: "true" + rbac.authorization.k8s.io/aggregate-to-view: "true" + name: flux-view +rules: + - apiGroups: + - notification.toolkit.fluxcd.io + - source.toolkit.fluxcd.io + - helm.toolkit.fluxcd.io + - image.toolkit.fluxcd.io + - kustomize.toolkit.fluxcd.io + - source.extensions.fluxcd.io + resources: + - '*' + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: flux +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: flux + namespace: cozy-fluxcd +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + app.kubernetes.io/role: cluster-admin + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + name: flux + namespace: cozy-fluxcd +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: flux + strategy: + type: Recreate + template: + metadata: + annotations: + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + prometheus.io/scrape: "true" + labels: + app.kubernetes.io/name: flux + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - flux + topologyKey: kubernetes.io/hostname + containers: + - args: + - --watch-all-namespaces + - --log-level=info + - --log-encoding=json + - --enable-leader-election=false + - --metrics-addr=:9791 + - --health-addr=:9792 + - --storage-addr=:9790 + - --storage-path=/data + - --storage-adv-addr=flux.$(RUNTIME_NAMESPACE).svc + - --concurrent=5 + - --requeue-dependency=30s + - --watch-label-selector=!sharding.fluxcd.io/key + - --helm-cache-max-size=10 + - --helm-cache-ttl=60m + - --helm-cache-purge-interval=5m + - --events-addr=http://localhost:9690 + env: + - name: SOURCE_CONTROLLER_LOCALHOST + value: localhost:9790 + - name: SOURCE_WATCHER_LOCALHOST + value: localhost:9691 + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + resource: limits.memory + - name: TUF_ROOT + value: /tmp/.sigstore + - name: NO_PROXY + value: .svc + image: ghcr.io/fluxcd/source-controller:v1.7.3 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz-sc + name: source-controller + ports: + - containerPort: 9790 + name: http-sc + protocol: TCP + - containerPort: 9791 + name: http-prom-sc + protocol: TCP + - containerPort: 9792 + name: healthz-sc + protocol: TCP + readinessProbe: + httpGet: + path: / + port: http-sc + resources: + limits: + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /data + name: data + - mountPath: /tmp + name: tmp + - args: + - --watch-all-namespaces + - --log-level=info + - --log-encoding=json + - --enable-leader-election=false + - --metrics-addr=:9793 + - --health-addr=:9794 + - --watch-label-selector=!sharding.fluxcd.io/key + - --concurrent=5 + - --requeue-dependency=30s + - --events-addr=http://localhost:9690 + - --feature-gates=ExternalArtifact=true + env: + - name: SOURCE_CONTROLLER_LOCALHOST + value: localhost:9790 + - name: SOURCE_WATCHER_LOCALHOST + value: localhost:9691 + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + resource: limits.memory + - name: TUF_ROOT + value: /tmp/.sigstore + - name: NO_PROXY + value: .svc + image: ghcr.io/fluxcd/kustomize-controller:v1.7.2 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz-kc + name: kustomize-controller + ports: + - containerPort: 9793 + name: http-prom-kc + protocol: TCP + - containerPort: 9794 + name: healthz-kc + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz-kc + resources: + limits: + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /tmp + name: tmp + - args: + - --watch-all-namespaces + - --log-level=info + - --log-encoding=json + - --enable-leader-election=false + - --metrics-addr=:9795 + - --health-addr=:9796 + - --watch-label-selector=!sharding.fluxcd.io/key + - --concurrent=5 + - --requeue-dependency=30s + - --events-addr=http://localhost:9690 + - --feature-gates=ExternalArtifact=true + env: + - name: SOURCE_CONTROLLER_LOCALHOST + value: localhost:9790 + - name: SOURCE_WATCHER_LOCALHOST + value: localhost:9691 + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + resource: limits.memory + - name: TUF_ROOT + value: /tmp/.sigstore + - name: NO_PROXY + value: .svc + image: ghcr.io/fluxcd/helm-controller:v1.4.3 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz-hc + name: helm-controller + ports: + - containerPort: 9795 + name: http-prom-hc + protocol: TCP + - containerPort: 9796 + name: healthz-hc + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz-hc + resources: + limits: + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /tmp + name: tmp + - args: + - --watch-all-namespaces + - --log-level=info + - --log-encoding=json + - --enable-leader-election=false + - --receiverAddr=:9797 + - --metrics-addr=:9798 + - --health-addr=:9799 + - --events-addr=:9690 + env: + - name: SOURCE_CONTROLLER_LOCALHOST + value: localhost:9790 + - name: SOURCE_WATCHER_LOCALHOST + value: localhost:9691 + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + resource: limits.memory + - name: TUF_ROOT + value: /tmp/.sigstore + - name: NO_PROXY + value: .svc + image: ghcr.io/fluxcd/notification-controller:v1.7.4 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz-nc + name: notification-controller + ports: + - containerPort: 9690 + name: http-nc + protocol: TCP + - containerPort: 9797 + name: http-webhook-nc + protocol: TCP + - containerPort: 9798 + name: http-prom-nc + protocol: TCP + - containerPort: 9799 + name: healthz-nc + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz-nc + resources: + limits: + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /tmp + name: tmp + - args: + - --watch-all-namespaces + - --log-level=info + - --log-encoding=json + - --enable-leader-election=false + - --metrics-addr=:9692 + - --health-addr=:9693 + - --storage-addr=:9691 + - --storage-path=/data + - --storage-adv-addr=source-watcher.$(RUNTIME_NAMESPACE).svc + - --events-addr=http://localhost:9690 + env: + - name: SOURCE_CONTROLLER_LOCALHOST + value: localhost:9790 + - name: SOURCE_WATCHER_LOCALHOST + value: localhost:9691 + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + resource: limits.memory + - name: TUF_ROOT + value: /tmp/.sigstore + - name: NO_PROXY + value: .svc + image: ghcr.io/fluxcd/source-watcher:v2.0.2 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz-sw + name: source-watcher + ports: + - containerPort: 9691 + name: http-sw + protocol: TCP + - containerPort: 9692 + name: http-prom-sw + protocol: TCP + - containerPort: 9693 + name: healthz-sw + protocol: TCP + readinessProbe: + httpGet: + path: / + port: http-sw + resources: + limits: + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /data + name: data + - mountPath: /tmp + name: tmp + hostNetwork: true + priorityClassName: system-cluster-critical + securityContext: + fsGroup: 1337 + serviceAccountName: flux + terminationGracePeriodSeconds: 120 + tolerations: + - key: node.kubernetes.io/not-ready + operator: Exists + - effect: NoExecute + key: node.kubernetes.io/unreachable + operator: Exists + tolerationSeconds: 300 + volumes: + - emptyDir: {} + name: data + - emptyDir: {} + name: tmp diff --git a/internal/lineagecontrollerwebhook/config.go b/internal/lineagecontrollerwebhook/config.go index 4ca45c13..1cb74442 100644 --- a/internal/lineagecontrollerwebhook/config.go +++ b/internal/lineagecontrollerwebhook/config.go @@ -2,49 +2,38 @@ package lineagecontrollerwebhook import ( "fmt" + "strings" - cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" helmv2 "github.com/fluxcd/helm-controller/api/v2" ) -type chartRef struct { - repo string - chart string -} - -type appRef struct { - group string - kind string -} - -type runtimeConfig struct { - chartAppMap map[chartRef]*cozyv1alpha1.CozystackResourceDefinition - appCRDMap map[appRef]*cozyv1alpha1.CozystackResourceDefinition -} - func (l *LineageControllerWebhook) initConfig() { - l.initOnce.Do(func() { - if l.config.Load() == nil { - l.config.Store(&runtimeConfig{ - chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition), - appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition), - }) - } - }) + // No longer needed - we use labels directly from HelmRelease } func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) { - cfg, ok := l.config.Load().(*runtimeConfig) + // Extract application metadata from labels + appKind, ok := hr.Labels["apps.cozystack.io/application.kind"] if !ok { - return "", "", "", fmt.Errorf("failed to load chart-app mapping from config") + return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app: missing apps.cozystack.io/application.kind label", hr.Namespace, hr.Name) } - if hr.Spec.Chart == nil { - return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name) - } - s := hr.Spec.Chart.Spec - val, ok := cfg.chartAppMap[chartRef{s.SourceRef.Name, s.Chart}] + + appGroup, ok := hr.Labels["apps.cozystack.io/application.group"] if !ok { - return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name) + return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app: missing apps.cozystack.io/application.group label", hr.Namespace, hr.Name) } - return "apps.cozystack.io/v1alpha1", val.Spec.Application.Kind, val.Spec.Release.Prefix, nil + + appName, ok := hr.Labels["apps.cozystack.io/application.name"] + if !ok { + return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app: missing apps.cozystack.io/application.name label", hr.Namespace, hr.Name) + } + + // Construct API version from group + apiVersion := fmt.Sprintf("%s/v1alpha1", appGroup) + + // Extract prefix from HelmRelease name by removing the application name + // HelmRelease name format: + prefix := strings.TrimSuffix(hr.Name, appName) + + return apiVersion, appKind, prefix, nil } diff --git a/internal/lineagecontrollerwebhook/controller.go b/internal/lineagecontrollerwebhook/controller.go index e7522f62..d3465429 100644 --- a/internal/lineagecontrollerwebhook/controller.go +++ b/internal/lineagecontrollerwebhook/controller.go @@ -1,54 +1,11 @@ package lineagecontrollerwebhook import ( - "context" - - cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/log" ) -// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=list;watch;get - +// SetupWithManagerAsController is no longer needed since we don't watch ApplicationDefinitions func (c *LineageControllerWebhook) SetupWithManagerAsController(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&cozyv1alpha1.CozystackResourceDefinition{}). - Complete(c) -} - -func (c *LineageControllerWebhook) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - l := log.FromContext(ctx) - crds := &cozyv1alpha1.CozystackResourceDefinitionList{} - if err := c.List(ctx, crds); err != nil { - l.Error(err, "failed reading CozystackResourceDefinitions") - return ctrl.Result{}, err - } - cfg := &runtimeConfig{ - chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition), - appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition), - } - for _, crd := range crds.Items { - chRef := chartRef{ - crd.Spec.Release.Chart.SourceRef.Name, - crd.Spec.Release.Chart.Name, - } - appRef := appRef{ - "apps.cozystack.io", - crd.Spec.Application.Kind, - } - - newRef := crd - if _, exists := cfg.chartAppMap[chRef]; exists { - l.Info("duplicate chart mapping detected; ignoring subsequent entry", "key", chRef) - } else { - cfg.chartAppMap[chRef] = &newRef - } - if _, exists := cfg.appCRDMap[appRef]; exists { - l.Info("duplicate app mapping detected; ignoring subsequent entry", "key", appRef) - } else { - cfg.appCRDMap[appRef] = &newRef - } - } - c.config.Store(cfg) - return ctrl.Result{}, nil + // No controller needed - we use labels directly from HelmRelease + return nil } diff --git a/internal/lineagecontrollerwebhook/matcher.go b/internal/lineagecontrollerwebhook/matcher.go index 7c756a49..6e7bd318 100644 --- a/internal/lineagecontrollerwebhook/matcher.go +++ b/internal/lineagecontrollerwebhook/matcher.go @@ -42,7 +42,7 @@ func matchName(ctx context.Context, name string, templateContext map[string]stri return false } -func matchResourceToSelector(ctx context.Context, name string, templateContext, l map[string]string, s *cozyv1alpha1.CozystackResourceDefinitionResourceSelector) bool { +func matchResourceToSelector(ctx context.Context, name string, templateContext, l map[string]string, s *cozyv1alpha1.ApplicationDefinitionResourceSelector) bool { sel, err := metav1.LabelSelectorAsSelector(&s.LabelSelector) if err != nil { log.FromContext(ctx).Error(err, "failed to convert label selector to selector") @@ -53,7 +53,7 @@ func matchResourceToSelector(ctx context.Context, name string, templateContext, return labelMatches && nameMatches } -func matchResourceToSelectorArray(ctx context.Context, name string, templateContext, l map[string]string, ss []*cozyv1alpha1.CozystackResourceDefinitionResourceSelector) bool { +func matchResourceToSelectorArray(ctx context.Context, name string, templateContext, l map[string]string, ss []*cozyv1alpha1.ApplicationDefinitionResourceSelector) bool { for _, s := range ss { if matchResourceToSelector(ctx, name, templateContext, l, s) { return true @@ -62,7 +62,7 @@ func matchResourceToSelectorArray(ctx context.Context, name string, templateCont return false } -func matchResourceToExcludeInclude(ctx context.Context, name string, templateContext, l map[string]string, resources *cozyv1alpha1.CozystackResourceDefinitionResources) bool { +func matchResourceToExcludeInclude(ctx context.Context, name string, templateContext, l map[string]string, resources *cozyv1alpha1.ApplicationDefinitionResources) bool { if resources == nil { return false } diff --git a/internal/lineagecontrollerwebhook/webhook.go b/internal/lineagecontrollerwebhook/webhook.go index 0841c891..ba8a6830 100644 --- a/internal/lineagecontrollerwebhook/webhook.go +++ b/internal/lineagecontrollerwebhook/webhook.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "strings" "github.com/cozystack/cozystack/pkg/lineage" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -33,8 +32,8 @@ const ( ManagerNameKey = "apps.cozystack.io/application.name" ) -// getResourceSelectors returns the appropriate CozystackResourceDefinitionResources for a given GroupKind -func (h *LineageControllerWebhook) getResourceSelectors(gk schema.GroupKind, crd *cozyv1alpha1.CozystackResourceDefinition) *cozyv1alpha1.CozystackResourceDefinitionResources { +// getResourceSelectors returns the appropriate ApplicationDefinitionResources for a given GroupKind +func (h *LineageControllerWebhook) getResourceSelectors(gk schema.GroupKind, crd *cozyv1alpha1.ApplicationDefinition) *cozyv1alpha1.ApplicationDefinitionResources { switch { case gk.Group == "" && gk.Kind == "Secret": return &crd.Spec.Secrets @@ -88,13 +87,16 @@ func (h *LineageControllerWebhook) Handle(ctx context.Context, req admission.Req "name", req.Name, "operation", req.Operation, ) + logger.Info("webhook called", "gvk", req.Kind.String(), "namespace", req.Namespace, "name", req.Name, "operation", req.Operation) warn := make(admission.Warnings, 0) obj := &unstructured.Unstructured{} if err := h.decodeUnstructured(req, obj); err != nil { + logger.Error(err, "failed to decode object") return admission.Errored(400, fmt.Errorf("decode object: %w", err)) } + logger.V(1).Info("decoded object", "labels", obj.GetLabels(), "ownerReferences", obj.GetOwnerReferences()) labels, err := h.computeLabels(ctx, obj) for { if err != nil && errors.Is(err, NoAncestors) { @@ -117,9 +119,10 @@ func (h *LineageControllerWebhook) Handle(ctx context.Context, req admission.Req mutated, err := json.Marshal(obj) if err != nil { - return admission.Errored(500, fmt.Errorf("marshal mutated pod: %w", err)) + logger.Error(err, "failed to marshal mutated object") + return admission.Errored(500, fmt.Errorf("marshal mutated object: %w", err)) } - logger.V(1).Info("mutated pod", "namespace", obj.GetNamespace(), "name", obj.GetName()) + logger.Info("mutated object", "namespace", obj.GetNamespace(), "name", obj.GetName(), "labels", labels) return admission.PatchResponseFromRaw(req.Object.Raw, mutated).WithWarnings(warn...) } @@ -156,21 +159,9 @@ func (h *LineageControllerWebhook) computeLabels(ctx context.Context, o *unstruc ManagerKindKey: obj.GetKind(), ManagerNameKey: obj.GetName(), } - templateLabels := map[string]string{ - "kind": strings.ToLower(obj.GetKind()), - "name": obj.GetName(), - "namespace": o.GetNamespace(), - } - cfg := h.config.Load().(*runtimeConfig) - crd := cfg.appCRDMap[appRef{gv.Group, obj.GetKind()}] - resourceSelectors := h.getResourceSelectors(o.GroupVersionKind().GroupKind(), crd) - - labels[corev1alpha1.TenantResourceLabelKey] = func(b bool) string { - if b { - return corev1alpha1.TenantResourceLabelValue - } - return "false" - }(matchResourceToExcludeInclude(ctx, o.GetName(), templateLabels, o.GetLabels(), resourceSelectors)) + // Resource selectors are no longer needed since we don't use ApplicationDefinitions + // Set tenant resource label to false by default (can be overridden by other logic if needed) + labels[corev1alpha1.TenantResourceLabelKey] = "false" return labels, err } diff --git a/internal/operator/bundle_reconciler.go b/internal/operator/bundle_reconciler.go new file mode 100644 index 00000000..ddaa1303 --- /dev/null +++ b/internal/operator/bundle_reconciler.go @@ -0,0 +1,1235 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operator + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + helmv2 "github.com/fluxcd/helm-controller/api/v2" + sourcewatcherv1beta1 "github.com/fluxcd/source-watcher/api/v2/v1beta1" + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// BundleReconciler reconciles Bundle resources +type BundleReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=cozystack.io,resources=bundles,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=cozystack.io,resources=bundles/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=source.extensions.fluxcd.io,resources=artifactgenerators,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=namespaces,verbs=get;list;watch;create;update;patch + +// Reconcile is part of the main kubernetes reconciliation loop +func (r *BundleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + bundle := &cozyv1alpha1.Bundle{} + if err := r.Get(ctx, req.NamespacedName, bundle); err != nil { + if apierrors.IsNotFound(err) { + // Cleanup orphaned resources + return r.cleanupOrphanedResources(ctx, req.NamespacedName) + } + return ctrl.Result{}, err + } + + // Resolve dependencies from other bundles + resolvedPackages, err := r.resolveDependencies(ctx, bundle) + if err != nil { + // If dependency bundle is not found, requeue to try again later + // Check if the error is wrapped IsNotFound + unwrappedErr := errors.Unwrap(err) + if unwrappedErr != nil && apierrors.IsNotFound(unwrappedErr) { + logger.Info("Dependency bundle not found, requeuing", "bundle", bundle.Name, "error", err) + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + } + logger.Error(err, "failed to resolve dependencies") + return ctrl.Result{}, err + } + + // Reconcile namespaces from packages + if err := r.reconcileNamespaces(ctx, bundle, resolvedPackages); err != nil { + logger.Error(err, "failed to reconcile namespaces") + return ctrl.Result{}, err + } + + // Check for conflicts between packages with artifact and artifacts + if err := r.checkArtifactConflicts(ctx, bundle, resolvedPackages); err != nil { + logger.Error(err, "failed to check artifact conflicts") + return ctrl.Result{}, err + } + + // Generate ArtifactGenerator for bundle (one generator per bundle with all OutputArtifacts) + if err := r.reconcileArtifactGenerators(ctx, bundle, resolvedPackages); err != nil { + logger.Error(err, "failed to reconcile ArtifactGenerator") + return ctrl.Result{}, err + } + + // Generate HelmReleases for packages + if err := r.reconcileHelmReleases(ctx, bundle, resolvedPackages); err != nil { + logger.Error(err, "failed to reconcile HelmReleases") + return ctrl.Result{}, err + } + + return ctrl.Result{}, nil +} + +// resolveDependencies resolves dependencies from other bundles +func (r *BundleReconciler) resolveDependencies(ctx context.Context, bundle *cozyv1alpha1.Bundle) ([]cozyv1alpha1.BundleRelease, error) { + resolved := make([]cozyv1alpha1.BundleRelease, 0, len(bundle.Spec.Packages)) + packageMap := make(map[string]bool) + + // Add all packages from this bundle + for _, pkg := range bundle.Spec.Packages { + if !pkg.Disabled { + resolved = append(resolved, pkg) + packageMap[pkg.Name] = true + } + } + + // Resolve dependencies from other bundles + for _, dependsOn := range bundle.Spec.DependsOn { + parts := strings.Split(dependsOn, "/") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid dependsOn format: %s (expected bundleName/target)", dependsOn) + } + + bundleName := parts[0] + targetName := parts[1] + + // Get the bundle + depBundle := &cozyv1alpha1.Bundle{} + if err := r.Get(ctx, types.NamespacedName{Name: bundleName}, depBundle); err != nil { + // If bundle is not found, return wrapped error so we can check it in Reconcile + if apierrors.IsNotFound(err) { + return nil, fmt.Errorf("failed to get bundle %s: %w", bundleName, err) + } + return nil, fmt.Errorf("failed to get bundle %s: %w", bundleName, err) + } + + // Find the target + var target *cozyv1alpha1.BundleDependencyTarget + for i := range depBundle.Spec.DependencyTargets { + if depBundle.Spec.DependencyTargets[i].Name == targetName { + target = &depBundle.Spec.DependencyTargets[i] + break + } + } + + if target == nil { + return nil, fmt.Errorf("target %s not found in bundle %s", targetName, bundleName) + } + + // Add packages from target to all packages in this bundle + for i := range resolved { + // Add target packages to dependsOn + for _, targetPkg := range target.Packages { + // Check if already in dependsOn + found := false + for _, dep := range resolved[i].DependsOn { + if dep == targetPkg { + found = true + break + } + } + if !found { + resolved[i].DependsOn = append(resolved[i].DependsOn, targetPkg) + } + } + } + } + + return resolved, nil +} + +// reconcileArtifactGenerators generates a single ArtifactGenerator for the bundle +// Creates one ArtifactGenerator per bundle with all OutputArtifacts from packages and artifacts +func (r *BundleReconciler) reconcileArtifactGenerators(ctx context.Context, bundle *cozyv1alpha1.Bundle, packages []cozyv1alpha1.BundleRelease) error { + logger := log.FromContext(ctx) + + libraryMap := make(map[string]cozyv1alpha1.BundleLibrary) + for _, lib := range bundle.Spec.Libraries { + libraryMap[lib.Name] = lib + } + + // Namespace is always cozy-system + namespace := "cozy-system" + // ArtifactGenerator name is the bundle name + agName := bundle.Name + + // Collect all OutputArtifacts + outputArtifacts := []sourcewatcherv1beta1.OutputArtifact{} + + // Process packages + for _, pkg := range packages { + logger.V(1).Info("processing package for artifact", "bundle", bundle.Name, "package", pkg.Name, "path", pkg.Path, "disabled", pkg.Disabled) + + // Skip packages without path (they might use artifacts) + if pkg.Path == "" { + logger.V(1).Info("skipping package without path", "name", pkg.Name) + continue + } + + // Extract package name from path (last component) + pkgName := r.getPackageNameFromPath(pkg.Path) + if pkgName == "" { + logger.Info("skipping package with invalid path", "name", pkg.Name, "path", pkg.Path) + continue + } + + logger.V(1).Info("extracted package name from path", "name", pkg.Name, "path", pkg.Path, "pkgName", pkgName) + + // Get basePath with default values + basePath := r.getBasePath(bundle) + + // Build copy operations + copyOps := []sourcewatcherv1beta1.CopyOperation{ + { + From: r.buildSourcePath(bundle.Spec.SourceRef.Name, basePath, pkg.Path), + To: fmt.Sprintf("@artifact/%s/", pkgName), + }, + } + + // Add libraries if specified + for _, libName := range pkg.Libraries { + if lib, ok := libraryMap[libName]; ok { + copyOps = append(copyOps, sourcewatcherv1beta1.CopyOperation{ + From: r.buildSourcePath(bundle.Spec.SourceRef.Name, basePath, lib.Path), + To: fmt.Sprintf("@artifact/%s/charts/%s/", pkgName, libName), + }) + } + } + + // Add valuesFiles if specified + for i, valuesFile := range pkg.ValuesFiles { + strategy := "Merge" + if i == 0 { + strategy = "Overwrite" + } + copyOps = append(copyOps, sourcewatcherv1beta1.CopyOperation{ + From: r.buildSourceFilePath(bundle.Spec.SourceRef.Name, basePath, fmt.Sprintf("%s/%s", pkg.Path, valuesFile)), + To: fmt.Sprintf("@artifact/%s/values.yaml", pkgName), + Strategy: strategy, + }) + } + + // Artifact name: bundle-name-package-name (e.g., cozystack-system-cilium) + artifactName := fmt.Sprintf("%s-%s", bundle.Name, pkgName) + + outputArtifacts = append(outputArtifacts, sourcewatcherv1beta1.OutputArtifact{ + Name: artifactName, + Copy: copyOps, + }) + + logger.Info("added OutputArtifact for package", "bundle", bundle.Name, "package", pkg.Name, "artifactName", artifactName) + } + + // Process artifacts + for _, artifact := range bundle.Spec.Artifacts { + logger.Info("processing artifact", "bundle", bundle.Name, "artifact", artifact.Name, "path", artifact.Path) + // Extract artifact name from path (last component) + artifactPathName := r.getPackageNameFromPath(artifact.Path) + if artifactPathName == "" { + logger.Info("skipping artifact with invalid path", "name", artifact.Name, "path", artifact.Path) + continue + } + + // Get basePath with default values + basePath := r.getBasePath(bundle) + + // Build copy operations + copyOps := []sourcewatcherv1beta1.CopyOperation{ + { + From: r.buildSourcePath(bundle.Spec.SourceRef.Name, basePath, artifact.Path), + To: fmt.Sprintf("@artifact/%s/", artifactPathName), + }, + } + + // Add libraries if specified + for _, libName := range artifact.Libraries { + if lib, ok := libraryMap[libName]; ok { + copyOps = append(copyOps, sourcewatcherv1beta1.CopyOperation{ + From: r.buildSourcePath(bundle.Spec.SourceRef.Name, basePath, lib.Path), + To: fmt.Sprintf("@artifact/%s/charts/%s/", artifactPathName, libName), + }) + } + } + + // Artifact name: {bundle-name}-{artifact-name} + artifactName := fmt.Sprintf("%s-%s", bundle.Name, artifact.Name) + + outputArtifacts = append(outputArtifacts, sourcewatcherv1beta1.OutputArtifact{ + Name: artifactName, + Copy: copyOps, + }) + + logger.Info("added OutputArtifact for artifact", "bundle", bundle.Name, "artifact", artifact.Name, "artifactName", artifactName) + } + + // If there are no OutputArtifacts, cleanup and return + if len(outputArtifacts) == 0 { + logger.Info("no OutputArtifacts to generate, skipping ArtifactGenerator creation", "bundle", bundle.Name) + // Cleanup orphaned ArtifactGenerators (to remove existing generator if it exists) + return r.cleanupOrphanedArtifactGenerators(ctx, bundle) + } + + // Build labels: merge bundle labels with default cozystack.io/bundle label + labels := make(map[string]string) + if bundle.Spec.Labels != nil { + for k, v := range bundle.Spec.Labels { + labels[k] = v + } + } + labels["cozystack.io/bundle"] = bundle.Name + + // Create single ArtifactGenerator for the bundle + ag := &sourcewatcherv1beta1.ArtifactGenerator{ + ObjectMeta: metav1.ObjectMeta{ + Name: agName, + Namespace: namespace, + Labels: labels, + }, + Spec: sourcewatcherv1beta1.ArtifactGeneratorSpec{ + Sources: []sourcewatcherv1beta1.SourceReference{ + { + Alias: bundle.Spec.SourceRef.Name, + Kind: bundle.Spec.SourceRef.Kind, + Name: bundle.Spec.SourceRef.Name, + Namespace: bundle.Spec.SourceRef.Namespace, + }, + }, + OutputArtifacts: outputArtifacts, + }, + } + + // Set ownerReference only if deletionPolicy is not Orphan + if bundle.Spec.DeletionPolicy != cozyv1alpha1.DeletionPolicyOrphan { + ag.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: bundle.APIVersion, + Kind: bundle.Kind, + Name: bundle.Name, + UID: bundle.UID, + Controller: func() *bool { b := true; return &b }(), + }, + } + } else { + // Explicitly set empty ownerReferences for Orphan policy + ag.OwnerReferences = []metav1.OwnerReference{} + } + + logger.Info("creating ArtifactGenerator for bundle", "bundle", bundle.Name, "agName", agName, "namespace", namespace, "outputArtifactCount", len(outputArtifacts)) + + if err := r.createOrUpdate(ctx, ag); err != nil { + return fmt.Errorf("failed to reconcile ArtifactGenerator %s: %w", agName, err) + } + + logger.Info("reconciled ArtifactGenerator for bundle", "name", agName, "namespace", namespace, "outputArtifactCount", len(outputArtifacts)) + + // Cleanup orphaned ArtifactGenerators + return r.cleanupOrphanedArtifactGenerators(ctx, bundle) +} + +// reconcileHelmReleases generates HelmReleases from bundle packages +func (r *BundleReconciler) reconcileHelmReleases(ctx context.Context, bundle *cozyv1alpha1.Bundle, packages []cozyv1alpha1.BundleRelease) error { + logger := log.FromContext(ctx) + + // Build package name map for dependency resolution (from current bundle) + packageNameMap := make(map[string]cozyv1alpha1.BundleRelease) + for _, pkg := range packages { + packageNameMap[pkg.Name] = pkg + } + + // Build global package name map from all bundles for finding dependencies + globalPackageMap := make(map[string]cozyv1alpha1.BundleRelease) + bundleList := &cozyv1alpha1.BundleList{} + if err := r.List(ctx, bundleList); err == nil { + for _, b := range bundleList.Items { + for _, pkg := range b.Spec.Packages { + // Only add if not already in map (first occurrence wins, or use current bundle's packages) + if _, exists := globalPackageMap[pkg.Name]; !exists { + globalPackageMap[pkg.Name] = pkg + } + } + } + } + // Override with packages from current bundle (they take precedence) + for _, pkg := range packages { + globalPackageMap[pkg.Name] = pkg + } + + // Build artifact name map from bundle artifacts for conflict checking + artifactNameMap := make(map[string]bool) + for _, artifact := range bundle.Spec.Artifacts { + artifactNameMap[artifact.Name] = true + } + + // Create HelmRelease for each package + for _, pkg := range packages { + // Skip disabled packages + if pkg.Disabled { + logger.V(1).Info("skipping disabled package", "name", pkg.Name, "namespace", pkg.Namespace) + continue + } + + var artifactName string + artifactNamespace := "cozy-system" + + if pkg.Artifact != "" { + // Package uses an artifact reference + // Check if artifact exists in bundle + if !artifactNameMap[pkg.Artifact] { + logger.Error(fmt.Errorf("artifact %s not found in bundle artifacts", pkg.Artifact), "skipping package", "name", pkg.Name) + continue + } + // Artifact name format: {bundle-name}-{artifact-name} + artifactName = fmt.Sprintf("%s-%s", bundle.Name, pkg.Artifact) + } else if pkg.Path != "" { + // Package uses a path + pkgName := r.getPackageNameFromPath(pkg.Path) + if pkgName == "" { + logger.Info("skipping package with invalid path", "name", pkg.Name, "path", pkg.Path) + continue + } + // Artifact name format: {bundle-name}-{package-name} + artifactName = fmt.Sprintf("%s-%s", bundle.Name, pkgName) + } else { + logger.Error(fmt.Errorf("neither artifact nor path specified"), "skipping package", "name", pkg.Name) + continue + } + + // Build labels: merge bundle labels, package labels, and default cozystack.io/bundle label + hrLabels := make(map[string]string) + // First, add bundle-level labels + if bundle.Spec.Labels != nil { + for k, v := range bundle.Spec.Labels { + hrLabels[k] = v + } + } + // Then, add package-level labels (they override bundle labels) + if pkg.Labels != nil { + for k, v := range pkg.Labels { + hrLabels[k] = v + } + } + // Finally, add default bundle label (it always takes precedence) + hrLabels["cozystack.io/bundle"] = bundle.Name + + // Add system-app label if namespace starts with "cozy-" + if pkg.Namespace == "kube-system" || strings.HasPrefix(pkg.Namespace, "cozy-") { + hrLabels["cozystack.io/system-app"] = "true" + } + + // Create HelmRelease + hr := &helmv2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: pkg.Name, + Namespace: pkg.Namespace, + Labels: hrLabels, + }, + Spec: helmv2.HelmReleaseSpec{ + Interval: metav1.Duration{Duration: 5 * 60 * 1000000000}, // 5m + ReleaseName: pkg.ReleaseName, + ChartRef: &helmv2.CrossNamespaceSourceReference{ + Kind: "ExternalArtifact", + Name: artifactName, + Namespace: artifactNamespace, + }, + Install: &helmv2.Install{ + Remediation: &helmv2.InstallRemediation{ + Retries: -1, + }, + }, + Upgrade: &helmv2.Upgrade{ + Remediation: &helmv2.UpgradeRemediation{ + Retries: -1, + }, + }, + }, + } + + // Set ownerReference only if deletionPolicy is not Orphan + if bundle.Spec.DeletionPolicy != cozyv1alpha1.DeletionPolicyOrphan { + hr.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: bundle.APIVersion, + Kind: bundle.Kind, + Name: bundle.Name, + UID: bundle.UID, + Controller: func() *bool { b := true; return &b }(), + }, + } + } else { + // Explicitly set empty ownerReferences for Orphan policy + hr.OwnerReferences = []metav1.OwnerReference{} + } + + // Set values if provided + if pkg.Values != nil { + hr.Spec.Values = pkg.Values + } + + // Add system-app label if TargetNamespace starts with "cozy-" + if hr.Spec.TargetNamespace != "" && (hr.Spec.TargetNamespace == "kube-system" || strings.HasPrefix(hr.Spec.TargetNamespace, "cozy-")) { + hr.Labels["cozystack.io/system-app"] = "true" + } + + // Set DependsOn + if len(pkg.DependsOn) > 0 { + dependsOn := make([]helmv2.DependencyReference, 0, len(pkg.DependsOn)) + for _, depName := range pkg.DependsOn { + depPkg, ok := globalPackageMap[depName] + if !ok { + logger.Info("dependent package not found in any bundle, using same namespace", "name", pkg.Name, "dependsOn", depName) + dependsOn = append(dependsOn, helmv2.DependencyReference{ + Name: depName, + Namespace: pkg.Namespace, + }) + } else { + dependsOn = append(dependsOn, helmv2.DependencyReference{ + Name: depPkg.Name, + Namespace: depPkg.Namespace, + }) + } + } + hr.Spec.DependsOn = dependsOn + } + + // Set valuesFiles annotation + if len(pkg.ValuesFiles) > 0 { + if hr.Annotations == nil { + hr.Annotations = make(map[string]string) + } + hr.Annotations["cozypkg.cozystack.io/values-files"] = strings.Join(pkg.ValuesFiles, ",") + } + + if err := r.createOrUpdate(ctx, hr); err != nil { + return fmt.Errorf("failed to reconcile HelmRelease %s: %w", pkg.Name, err) + } + logger.Info("reconciled HelmRelease", "name", pkg.Name, "namespace", pkg.Namespace) + } + + // Cleanup orphaned HelmReleases + return r.cleanupOrphanedHelmReleases(ctx, bundle) +} + +// createOrUpdate creates or updates a resource +func (r *BundleReconciler) createOrUpdate(ctx context.Context, obj client.Object) error { + existing := obj.DeepCopyObject().(client.Object) + key := client.ObjectKeyFromObject(obj) + + err := r.Get(ctx, key, existing) + if apierrors.IsNotFound(err) { + return r.Create(ctx, obj) + } else if err != nil { + return err + } + + // Preserve resource version + obj.SetResourceVersion(existing.GetResourceVersion()) + // Merge labels and annotations + labels := obj.GetLabels() + if labels == nil { + labels = make(map[string]string) + } + for k, v := range existing.GetLabels() { + if _, ok := labels[k]; !ok { + labels[k] = v + } + } + obj.SetLabels(labels) + + annotations := obj.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + for k, v := range existing.GetAnnotations() { + if _, ok := annotations[k]; !ok { + annotations[k] = v + } + } + obj.SetAnnotations(annotations) + + // Update ownerReferences: always use the ones from obj + // This allows deletionPolicy = Orphan to work by setting empty ownerReferences array + // When ownerReferences field is set in obj (even as empty array), use it + // Empty array will clear ownerReferences (for deletionPolicy = Orphan or when policy changes) + // If ownerReferences field is not set in obj (nil), preserve existing ones + objOwnerRefs := obj.GetOwnerReferences() + if objOwnerRefs != nil { + // obj has ownerReferences set (either populated or empty array), use them + // Empty array (len == 0) means we want to remove all ownerReferences (deletionPolicy = Orphan) + // This handles policy changes from Delete to Orphan + // objOwnerRefs is already set in obj, so it will be used in Update + // No need to do anything else - Update will use the ownerReferences from obj + } else if len(existing.GetOwnerReferences()) > 0 { + // obj doesn't have ownerReferences set (nil), but existing does + // Preserve existing ones (they might be from other owners) + obj.SetOwnerReferences(existing.GetOwnerReferences()) + } + + // For ArtifactGenerator, explicitly update Spec (OutputArtifacts and Sources) + // This ensures that OutputArtifacts from both packages and artifacts are properly updated + if ag, ok := obj.(*sourcewatcherv1beta1.ArtifactGenerator); ok { + if existingAG, ok := existing.(*sourcewatcherv1beta1.ArtifactGenerator); ok { + logger := log.FromContext(ctx) + logger.V(1).Info("updating ArtifactGenerator Spec", "name", ag.Name, "namespace", ag.Namespace, + "outputArtifactCount", len(ag.Spec.OutputArtifacts)) + // Update Spec from obj (which contains the desired state with all OutputArtifacts) + existingAG.Spec = ag.Spec + // Preserve metadata updates we made above + existingAG.SetLabels(ag.GetLabels()) + existingAG.SetAnnotations(ag.GetAnnotations()) + existingAG.SetOwnerReferences(ag.GetOwnerReferences()) + // Use existingAG for Update + obj = existingAG + } + } + + // For HelmRelease, explicitly update Spec to ensure values and dependsOn are properly updated + if hr, ok := obj.(*helmv2.HelmRelease); ok { + if existingHR, ok := existing.(*helmv2.HelmRelease); ok { + logger := log.FromContext(ctx) + logger.V(1).Info("updating HelmRelease Spec", "name", hr.Name, "namespace", hr.Namespace) + + // Check if this HelmRelease is managed through Application API or Controller + // If it has apps.cozystack.io/application.* labels OR cozystack.io/ui=true label, merge values with bundle priority + isApplicationManaged := existingHR.Labels["apps.cozystack.io/application.kind"] != "" && + existingHR.Labels["apps.cozystack.io/application.group"] != "" + isControllerManaged := existingHR.Labels["cozystack.io/ui"] == "true" + + if isApplicationManaged || isControllerManaged { + // For Application/Controller-managed HelmReleases, merge values with bundle priority + logger.V(1).Info("merging values for Application/Controller-managed HelmRelease with bundle priority", "name", hr.Name, "namespace", hr.Namespace, "isApplicationManaged", isApplicationManaged, "isControllerManaged", isControllerManaged) + existingHR.Spec.Chart = hr.Spec.Chart + existingHR.Spec.ChartRef = hr.Spec.ChartRef + existingHR.Spec.Interval = hr.Spec.Interval + existingHR.Spec.Timeout = hr.Spec.Timeout + existingHR.Spec.ReleaseName = hr.Spec.ReleaseName + existingHR.Spec.DependsOn = hr.Spec.DependsOn + existingHR.Spec.Install = hr.Spec.Install + existingHR.Spec.Upgrade = hr.Spec.Upgrade + existingHR.Spec.Uninstall = hr.Spec.Uninstall + existingHR.Spec.Rollback = hr.Spec.Rollback + existingHR.Spec.StorageNamespace = hr.Spec.StorageNamespace + existingHR.Spec.KubeConfig = hr.Spec.KubeConfig + existingHR.Spec.TargetNamespace = hr.Spec.TargetNamespace + existingHR.Spec.PostRenderers = hr.Spec.PostRenderers + existingHR.Spec.ServiceAccountName = hr.Spec.ServiceAccountName + existingHR.Spec.Suspend = hr.Spec.Suspend + + // Merge values: bundle values have priority (override existing) + mergedValues, err := mergeHelmReleaseValuesWithBundlePriority(existingHR.Spec.Values, hr.Spec.Values) + if err != nil { + logger.Error(err, "failed to merge values, using bundle values", "name", hr.Name, "namespace", hr.Namespace) + existingHR.Spec.Values = hr.Spec.Values + } else { + existingHR.Spec.Values = mergedValues + } + } else { + // For bundle-managed HelmReleases, update everything including values + existingHR.Spec = hr.Spec + } + + // Preserve metadata updates we made above + existingHR.SetLabels(hr.GetLabels()) + existingHR.SetAnnotations(hr.GetAnnotations()) + existingHR.SetOwnerReferences(hr.GetOwnerReferences()) + // Use existingHR for Update + obj = existingHR + } + } + + return r.Update(ctx, obj) +} + +// mergeHelmReleaseValues merges two HelmRelease values JSON objects +// Existing values have priority (bundle values are merged into existing) +func mergeHelmReleaseValues(existingValues, bundleValues *apiextensionsv1.JSON) (*apiextensionsv1.JSON, error) { + // If bundle has no values, preserve existing + if bundleValues == nil || len(bundleValues.Raw) == 0 { + return existingValues, nil + } + + // If existing has no values, use bundle values + if existingValues == nil || len(existingValues.Raw) == 0 { + return bundleValues, nil + } + + // Parse both values + var existingMap map[string]interface{} + if err := json.Unmarshal(existingValues.Raw, &existingMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal existing values: %w", err) + } + + var bundleMap map[string]interface{} + if err := json.Unmarshal(bundleValues.Raw, &bundleMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal bundle values: %w", err) + } + + // Merge: existing values have priority (bundle is merged into existing) + mergedMap := deepMergeMaps(bundleMap, existingMap) + + // Marshal back to JSON + mergedJSON, err := json.Marshal(mergedMap) + if err != nil { + return nil, fmt.Errorf("failed to marshal merged values: %w", err) + } + + return &apiextensionsv1.JSON{Raw: mergedJSON}, nil +} + +// mergeHelmReleaseValuesWithBundlePriority merges two HelmRelease values JSON objects +// Bundle values have priority (override existing values) +// All fields from bundle override existing, except nested merges for maps +func mergeHelmReleaseValuesWithBundlePriority(existingValues, bundleValues *apiextensionsv1.JSON) (*apiextensionsv1.JSON, error) { + // If bundle has no values, preserve existing + if bundleValues == nil || len(bundleValues.Raw) == 0 { + return existingValues, nil + } + + // If existing has no values, use bundle values + if existingValues == nil || len(existingValues.Raw) == 0 { + return bundleValues, nil + } + + // Parse both values + var existingMap map[string]interface{} + if err := json.Unmarshal(existingValues.Raw, &existingMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal existing values: %w", err) + } + + var bundleMap map[string]interface{} + if err := json.Unmarshal(bundleValues.Raw, &bundleMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal bundle values: %w", err) + } + + // Merge: start with existing values, then bundle values override (bundle has priority) + mergedMap := deepMergeMaps(existingMap, bundleMap) + + // Marshal back to JSON + mergedJSON, err := json.Marshal(mergedMap) + if err != nil { + return nil, fmt.Errorf("failed to marshal merged values: %w", err) + } + + return &apiextensionsv1.JSON{Raw: mergedJSON}, nil +} + +// deepMergeMaps performs a deep merge of two maps +// Values from override map take precedence, but nested maps are merged recursively +func deepMergeMaps(base, override map[string]interface{}) map[string]interface{} { + result := make(map[string]interface{}) + + // Copy base map + for k, v := range base { + result[k] = v + } + + // Merge override map + for k, v := range override { + if baseVal, exists := result[k]; exists { + // If both are maps, recursively merge + if baseMap, ok := baseVal.(map[string]interface{}); ok { + if overrideMap, ok := v.(map[string]interface{}); ok { + result[k] = deepMergeMaps(baseMap, overrideMap) + continue + } + } + } + // Override takes precedence for non-map values or new keys + result[k] = v + } + + return result +} + +// Helper functions +func (r *BundleReconciler) getPackageNameFromPath(path string) string { + parts := strings.Split(path, "/") + if len(parts) > 0 { + return parts[len(parts)-1] + } + return "" +} + +// getBasePath returns the basePath with default values based on source kind +func (r *BundleReconciler) getBasePath(bundle *cozyv1alpha1.Bundle) string { + // If basePath is explicitly set, use it + if bundle.Spec.BasePath != "" { + return bundle.Spec.BasePath + } + // Default values based on kind + if bundle.Spec.SourceRef.Kind == "OCIRepository" { + return "" // Root for OCI + } + // Default for GitRepository + return "packages" +} + +// buildSourcePath builds the full source path using basePath with glob pattern +func (r *BundleReconciler) buildSourcePath(sourceName, basePath, path string) string { + // Remove leading/trailing slashes and combine + parts := []string{} + if basePath != "" { + parts = append(parts, strings.Trim(basePath, "/")) + } + if path != "" { + parts = append(parts, strings.Trim(path, "/")) + } + + fullPath := strings.Join(parts, "/") + if fullPath == "" { + return fmt.Sprintf("@%s/**", sourceName) + } + return fmt.Sprintf("@%s/%s/**", sourceName, fullPath) +} + +// buildSourceFilePath builds the full source path for a specific file (without glob pattern) +func (r *BundleReconciler) buildSourceFilePath(sourceName, basePath, path string) string { + // Remove leading/trailing slashes and combine + parts := []string{} + if basePath != "" { + parts = append(parts, strings.Trim(basePath, "/")) + } + if path != "" { + parts = append(parts, strings.Trim(path, "/")) + } + + fullPath := strings.Join(parts, "/") + if fullPath == "" { + return fmt.Sprintf("@%s", sourceName) + } + return fmt.Sprintf("@%s/%s", sourceName, fullPath) +} + +func (r *BundleReconciler) cleanupOrphanedArtifactGenerators(ctx context.Context, bundle *cozyv1alpha1.Bundle) error { + logger := log.FromContext(ctx) + + // Find ArtifactGenerators by label + agList := &sourcewatcherv1beta1.ArtifactGeneratorList{} + if err := r.List(ctx, agList, client.MatchingLabels{ + "cozystack.io/bundle": bundle.Name, + }); err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return err + } + + // Desired name: bundle name (one ArtifactGenerator per bundle) + desiredName := bundle.Name + + // Find ArtifactGenerators with this bundle label + for _, ag := range agList.Items { + // Check if it's the desired name + isDesired := ag.Name == desiredName + + if !isDesired { + // Delete ArtifactGenerators that don't match the desired name + // This includes old pattern ArtifactGenerators (for migration from per-package/per-artifact to per-bundle) + logger.Info("deleting orphaned ArtifactGenerator", "name", ag.Name, "bundle", bundle.Name, "desiredName", desiredName) + if err := r.Delete(ctx, &ag); err != nil { + if !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete orphaned ArtifactGenerator", "name", ag.Name) + } + } else { + logger.Info("deleted orphaned ArtifactGenerator", "name", ag.Name) + } + } + } + + return nil +} + +func (r *BundleReconciler) cleanupOrphanedHelmReleases(ctx context.Context, bundle *cozyv1alpha1.Bundle) error { + logger := log.FromContext(ctx) + + // Find HelmReleases by label + hrList := &helmv2.HelmReleaseList{} + if err := r.List(ctx, hrList, client.MatchingLabels{ + "cozystack.io/bundle": bundle.Name, + }); err != nil { + return err + } + + // Build desired names (excluding disabled packages) + desiredNames := make(map[types.NamespacedName]bool) + for _, pkg := range bundle.Spec.Packages { + // Only include non-disabled packages in desired names + if !pkg.Disabled { + desiredNames[types.NamespacedName{Name: pkg.Name, Namespace: pkg.Namespace}] = true + } + } + + // Find HelmReleases with this bundle label + for _, hr := range hrList.Items { + key := types.NamespacedName{Name: hr.Name, Namespace: hr.Namespace} + if !desiredNames[key] { + logger.Info("deleting orphaned HelmRelease", "name", hr.Name, "namespace", hr.Namespace, "bundle", bundle.Name) + if err := r.Delete(ctx, &hr); err != nil { + if !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete HelmRelease", "name", hr.Name, "namespace", hr.Namespace) + } + } else { + logger.Info("deleted orphaned HelmRelease", "name", hr.Name, "namespace", hr.Namespace) + } + } + } + + return nil +} + +func (r *BundleReconciler) cleanupOrphanedResources(ctx context.Context, bundleKey types.NamespacedName) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + // Cleanup ArtifactGenerators by label + // Only delete if they have ownerReferences to this bundle (deletionPolicy != Orphan) + agList := &sourcewatcherv1beta1.ArtifactGeneratorList{} + if err := r.List(ctx, agList, client.MatchingLabels{ + "cozystack.io/bundle": bundleKey.Name, + }); err == nil { + for _, ag := range agList.Items { + // Check if this resource has ownerReference to the deleted bundle + hasOwnerRef := false + for _, ownerRef := range ag.OwnerReferences { + if ownerRef.Kind == "Bundle" && ownerRef.Name == bundleKey.Name { + hasOwnerRef = true + break + } + } + + // Only delete if it has ownerReference (deletionPolicy != Orphan) + // If no ownerReference, it means deletionPolicy was Orphan, so we should not delete it + if hasOwnerRef { + logger.Info("deleting orphaned ArtifactGenerator", "name", ag.Name, "bundle", bundleKey.Name) + if err := r.Delete(ctx, &ag); err != nil && !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete orphaned ArtifactGenerator", "name", ag.Name) + } + } else { + logger.Info("skipping ArtifactGenerator deletion (deletionPolicy=Orphan)", "name", ag.Name, "bundle", bundleKey.Name) + } + } + } + + // Cleanup HelmReleases by label + // Only delete if they have ownerReferences to this bundle (deletionPolicy != Orphan) + hrList := &helmv2.HelmReleaseList{} + if err := r.List(ctx, hrList, client.MatchingLabels{ + "cozystack.io/bundle": bundleKey.Name, + }); err == nil { + for _, hr := range hrList.Items { + // Check if this resource has ownerReference to the deleted bundle + hasOwnerRef := false + for _, ownerRef := range hr.OwnerReferences { + if ownerRef.Kind == "Bundle" && ownerRef.Name == bundleKey.Name { + hasOwnerRef = true + break + } + } + + // Only delete if it has ownerReference (deletionPolicy != Orphan) + // If no ownerReference, it means deletionPolicy was Orphan, so we should not delete it + if hasOwnerRef { + logger.Info("deleting orphaned HelmRelease", "name", hr.Name, "namespace", hr.Namespace, "bundle", bundleKey.Name) + if err := r.Delete(ctx, &hr); err != nil && !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete orphaned HelmRelease", "name", hr.Name, "namespace", hr.Namespace) + } + } else { + logger.Info("skipping HelmRelease deletion (deletionPolicy=Orphan)", "name", hr.Name, "namespace", hr.Namespace, "bundle", bundleKey.Name) + } + } + } + + return ctrl.Result{}, nil +} + +// reconcileNamespaces creates or updates namespaces based on packages in the bundle. +func (r *BundleReconciler) reconcileNamespaces(ctx context.Context, bundle *cozyv1alpha1.Bundle, packages []cozyv1alpha1.BundleRelease) error { + logger := log.FromContext(ctx) + + // Collect namespaces from packages + // Map: namespace -> {isPrivileged, labels, annotations} + type namespaceInfo struct { + privileged bool + labels map[string]string + annotations map[string]string + } + namespacesMap := make(map[string]namespaceInfo) + + for _, pkg := range packages { + // Skip disabled packages + if pkg.Disabled { + continue + } + + // Skip if namespace is empty + if pkg.Namespace == "" { + continue + } + + info, exists := namespacesMap[pkg.Namespace] + if !exists { + info = namespaceInfo{ + privileged: false, + labels: make(map[string]string), + annotations: make(map[string]string), + } + } + + // If package is privileged, mark namespace as privileged + if pkg.Privileged { + info.privileged = true + } + + // Merge namespace labels from package + if pkg.NamespaceLabels != nil { + for k, v := range pkg.NamespaceLabels { + info.labels[k] = v + } + } + + // Merge namespace annotations from package + if pkg.NamespaceAnnotations != nil { + for k, v := range pkg.NamespaceAnnotations { + info.annotations[k] = v + } + } + + namespacesMap[pkg.Namespace] = info + } + + // Create or update all namespaces + for nsName, info := range namespacesMap { + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsName, + Labels: make(map[string]string), + Annotations: map[string]string{ + "helm.sh/resource-policy": "keep", + }, + }, + } + + // Add system label only for non-tenant namespaces + if !strings.HasPrefix(nsName, "tenant-") { + namespace.Labels["cozystack.io/system"] = "true" + } + + // Add privileged label if needed + if info.privileged { + namespace.Labels["pod-security.kubernetes.io/enforce"] = "privileged" + } + + // Merge namespace labels from packages + for k, v := range info.labels { + namespace.Labels[k] = v + } + + // Merge namespace annotations from packages + for k, v := range info.annotations { + namespace.Annotations[k] = v + } + + if err := r.createOrUpdateNamespace(ctx, namespace); err != nil { + logger.Error(err, "failed to reconcile namespace", "name", nsName, "privileged", info.privileged) + return fmt.Errorf("failed to reconcile namespace %s: %w", nsName, err) + } + logger.Info("reconciled namespace", "name", nsName, "privileged", info.privileged, "labels", info.labels, "annotations", info.annotations) + } + + return nil +} + +// createOrUpdateNamespace creates or updates a namespace. +func (r *BundleReconciler) createOrUpdateNamespace(ctx context.Context, namespace *corev1.Namespace) error { + existing := &corev1.Namespace{} + key := types.NamespacedName{Name: namespace.Name} + + err := r.Get(ctx, key, existing) + if apierrors.IsNotFound(err) { + return r.Create(ctx, namespace) + } else if err != nil { + return err + } + + // Preserve resource version + namespace.SetResourceVersion(existing.GetResourceVersion()) + + // Merge labels + labels := namespace.GetLabels() + if labels == nil { + labels = make(map[string]string) + } + for k, v := range existing.GetLabels() { + if _, ok := labels[k]; !ok { + labels[k] = v + } + } + namespace.SetLabels(labels) + + // Merge annotations + annotations := namespace.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + for k, v := range existing.GetAnnotations() { + if _, ok := annotations[k]; !ok { + annotations[k] = v + } + } + namespace.SetAnnotations(annotations) + + return r.Update(ctx, namespace) +} + +// checkArtifactConflicts checks for conflicts between packages using artifacts and bundle artifacts +func (r *BundleReconciler) checkArtifactConflicts(ctx context.Context, bundle *cozyv1alpha1.Bundle, packages []cozyv1alpha1.BundleRelease) error { + // Build artifact name map from bundle artifacts + artifactNameMap := make(map[string]bool) + for _, artifact := range bundle.Spec.Artifacts { + artifactNameMap[artifact.Name] = true + } + + // Check packages that use artifacts + for _, pkg := range packages { + if pkg.Artifact != "" { + if !artifactNameMap[pkg.Artifact] { + return fmt.Errorf("package %s references artifact %s which is not defined in bundle artifacts", pkg.Name, pkg.Artifact) + } + } + } + + return nil +} + +// removeOwnerReferences removes ownerReferences from all resources with bundle label +func (r *BundleReconciler) removeOwnerReferences(ctx context.Context, bundle *cozyv1alpha1.Bundle) error { + logger := log.FromContext(ctx) + + // Remove ownerReferences from ArtifactGenerators by label + agList := &sourcewatcherv1beta1.ArtifactGeneratorList{} + if err := r.List(ctx, agList, client.MatchingLabels{ + "cozystack.io/bundle": bundle.Name, + }); err == nil { + for i := range agList.Items { + ag := &agList.Items[i] + updated := false + newOwnerRefs := []metav1.OwnerReference{} + + for _, ownerRef := range ag.OwnerReferences { + if ownerRef.Kind == "Bundle" && ownerRef.Name == bundle.Name { + // Skip this ownerReference (remove it) + // Check by name only, not UID, to handle bundle updates + updated = true + } else { + // Keep other ownerReferences + newOwnerRefs = append(newOwnerRefs, ownerRef) + } + } + + if updated { + ag.SetOwnerReferences(newOwnerRefs) + if err := r.Update(ctx, ag); err != nil { + logger.Error(err, "failed to remove ownerReference from ArtifactGenerator", "name", ag.Name, "namespace", ag.Namespace) + } else { + logger.Info("removed ownerReference from ArtifactGenerator", "name", ag.Name, "namespace", ag.Namespace) + } + } + } + } + + // Remove ownerReferences from HelmReleases by label + hrList := &helmv2.HelmReleaseList{} + if err := r.List(ctx, hrList, client.MatchingLabels{ + "cozystack.io/bundle": bundle.Name, + }); err == nil { + for i := range hrList.Items { + hr := &hrList.Items[i] + updated := false + newOwnerRefs := []metav1.OwnerReference{} + + for _, ownerRef := range hr.OwnerReferences { + if ownerRef.Kind == "Bundle" && ownerRef.Name == bundle.Name { + // Skip this ownerReference (remove it) + // Check by name only, not UID, to handle bundle updates + updated = true + } else { + // Keep other ownerReferences + newOwnerRefs = append(newOwnerRefs, ownerRef) + } + } + + if updated { + hr.SetOwnerReferences(newOwnerRefs) + if err := r.Update(ctx, hr); err != nil { + logger.Error(err, "failed to remove ownerReference from HelmRelease", "name", hr.Name, "namespace", hr.Namespace) + } else { + logger.Info("removed ownerReference from HelmRelease", "name", hr.Name, "namespace", hr.Namespace) + } + } + } + } + + return nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *BundleReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + Named("cozystack-bundle"). + For(&cozyv1alpha1.Bundle{}). + Watches( + &helmv2.HelmRelease{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + hr, ok := obj.(*helmv2.HelmRelease) + if !ok { + return nil + } + // Find the bundle that owns this HelmRelease by label + bundleName := hr.Labels["cozystack.io/bundle"] + if bundleName == "" { + return nil + } + // Reconcile the bundle to recreate the HelmRelease if it was deleted + return []reconcile.Request{{ + NamespacedName: types.NamespacedName{ + Name: bundleName, + }, + }} + }), + ). + Complete(r) +} diff --git a/internal/operator/platform_reconciler.go b/internal/operator/platform_reconciler.go new file mode 100644 index 00000000..1a8c0aa5 --- /dev/null +++ b/internal/operator/platform_reconciler.go @@ -0,0 +1,541 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package operator + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + helmv2 "github.com/fluxcd/helm-controller/api/v2" + sourcewatcherv1beta1 "github.com/fluxcd/source-watcher/api/v2/v1beta1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// PlatformReconciler reconciles Platform resources +type PlatformReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=cozystack.io,resources=platforms,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=cozystack.io,resources=platforms/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=source.extensions.fluxcd.io,resources=artifactgenerators,verbs=get;list;watch;create;update;patch;delete + +// Reconcile is part of the main kubernetes reconciliation loop +func (r *PlatformReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + platform := &cozyv1alpha1.Platform{} + if err := r.Get(ctx, req.NamespacedName, platform); err != nil { + if apierrors.IsNotFound(err) { + // Cleanup orphaned resources + return r.cleanupOrphanedResources(ctx, req.NamespacedName) + } + return ctrl.Result{}, err + } + + // Set defaults + if platform.Spec.Interval == nil { + platform.Spec.Interval = &metav1.Duration{Duration: 5 * 60 * 1000000000} // 5m + } + + // Reconcile ArtifactGenerator + if err := r.reconcileArtifactGenerator(ctx, platform); err != nil { + logger.Error(err, "failed to reconcile ArtifactGenerator") + return ctrl.Result{}, err + } + + // Reconcile HelmRelease + if err := r.reconcileHelmRelease(ctx, platform); err != nil { + logger.Error(err, "failed to reconcile HelmRelease") + return ctrl.Result{}, err + } + + // Cleanup orphaned resources with platform label + if err := r.cleanupOrphanedPlatformResources(ctx, platform); err != nil { + logger.Error(err, "failed to cleanup orphaned platform resources") + // Don't return error, just log it - cleanup is best effort + } + + return ctrl.Result{}, nil +} + +// reconcileArtifactGenerator creates or updates the ArtifactGenerator for the platform +func (r *PlatformReconciler) reconcileArtifactGenerator(ctx context.Context, platform *cozyv1alpha1.Platform) error { + logger := log.FromContext(ctx) + + // Use fixed namespace for cluster-scoped resource + namespace := "cozy-system" + + // Get basePath with default values (already includes full path to platform) + basePath := r.getBasePath(platform) + + // Build full path from basePath (basePath already contains the full path) + fullPath := r.buildSourcePath(platform.Spec.SourceRef.Name, basePath, "") + // Extract the last component for the artifact name + artifactPathParts := strings.Split(strings.Trim(basePath, "/"), "/") + artifactName := artifactPathParts[len(artifactPathParts)-1] + + copyOps := []sourcewatcherv1beta1.CopyOperation{ + { + From: fullPath + "/**", + To: fmt.Sprintf("@artifact/%s/", artifactName), + }, + } + + // Create ArtifactGenerator + ag := &sourcewatcherv1beta1.ArtifactGenerator{ + ObjectMeta: metav1.ObjectMeta{ + Name: platform.Name, + Namespace: namespace, + Labels: map[string]string{ + "cozystack.io/platform": platform.Name, + }, + }, + Spec: sourcewatcherv1beta1.ArtifactGeneratorSpec{ + Sources: []sourcewatcherv1beta1.SourceReference{ + { + Alias: platform.Spec.SourceRef.Name, + Kind: platform.Spec.SourceRef.Kind, + Name: platform.Spec.SourceRef.Name, + Namespace: platform.Spec.SourceRef.Namespace, + }, + }, + OutputArtifacts: []sourcewatcherv1beta1.OutputArtifact{ + { + Name: artifactName, + Copy: copyOps, + }, + }, + }, + } + + // Set ownerReference + ag.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: platform.APIVersion, + Kind: platform.Kind, + Name: platform.Name, + UID: platform.UID, + Controller: func() *bool { b := true; return &b }(), + }, + } + + logger.Info("reconciling ArtifactGenerator", "name", platform.Name, "namespace", namespace) + + if err := r.createOrUpdate(ctx, ag); err != nil { + return fmt.Errorf("failed to reconcile ArtifactGenerator %s: %w", platform.Name, err) + } + + logger.Info("reconciled ArtifactGenerator", "name", platform.Name, "namespace", namespace) + return nil +} + +// reconcileHelmRelease creates or updates the HelmRelease for the platform +func (r *PlatformReconciler) reconcileHelmRelease(ctx context.Context, platform *cozyv1alpha1.Platform) error { + logger := log.FromContext(ctx) + + // HelmRelease name is fixed: cozystack-platform + // Use fixed namespace for cluster-scoped resource + namespace := "cozy-system" + + // Get artifact name (last component of basePath) + basePath := r.getBasePath(platform) + artifactPathParts := strings.Split(strings.Trim(basePath, "/"), "/") + artifactName := artifactPathParts[len(artifactPathParts)-1] + + // Merge values with sourceRef + values := r.mergeValuesWithSourceRef(platform.Spec.Values, platform.Spec.SourceRef) + + // Create HelmRelease + hr := &helmv2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: platform.Name, + Namespace: namespace, + Labels: map[string]string{ + "cozystack.io/platform": platform.Name, + }, + }, + Spec: helmv2.HelmReleaseSpec{ + Interval: *platform.Spec.Interval, + TargetNamespace: "cozy-system", + ReleaseName: "cozystack-platform", + ChartRef: &helmv2.CrossNamespaceSourceReference{ + Kind: "ExternalArtifact", + Name: artifactName, + Namespace: namespace, + }, + Values: values, + Install: &helmv2.Install{ + Remediation: &helmv2.InstallRemediation{ + Retries: -1, + }, + }, + Upgrade: &helmv2.Upgrade{ + Remediation: &helmv2.UpgradeRemediation{ + Retries: -1, + }, + }, + }, + } + + // Set ownerReference + hr.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: platform.APIVersion, + Kind: platform.Kind, + Name: platform.Name, + UID: platform.UID, + Controller: func() *bool { b := true; return &b }(), + }, + } + + logger.Info("reconciling HelmRelease", "name", platform.Name, "namespace", namespace) + + if err := r.createOrUpdate(ctx, hr); err != nil { + return fmt.Errorf("failed to reconcile HelmRelease %s: %w", platform.Name, err) + } + + logger.Info("reconciled HelmRelease", "name", platform.Name, "namespace", namespace) + return nil +} + +// mergeValuesWithSourceRef merges platform values with sourceRef +func (r *PlatformReconciler) mergeValuesWithSourceRef(values *apiextensionsv1.JSON, sourceRef cozyv1alpha1.SourceRef) *apiextensionsv1.JSON { + // Build sourceRef map + sourceRefMap := map[string]interface{}{ + "kind": sourceRef.Kind, + "name": sourceRef.Name, + "namespace": sourceRef.Namespace, + } + + // If values is nil or empty, create new values with sourceRef + if values == nil || len(values.Raw) == 0 { + valuesMap := map[string]interface{}{ + "sourceRef": sourceRefMap, + } + raw, _ := json.Marshal(valuesMap) + return &apiextensionsv1.JSON{Raw: raw} + } + + // Parse existing values + var valuesMap map[string]interface{} + if err := json.Unmarshal(values.Raw, &valuesMap); err != nil { + // If unmarshal fails, create new values with sourceRef + valuesMap = map[string]interface{}{ + "sourceRef": sourceRefMap, + } + raw, _ := json.Marshal(valuesMap) + return &apiextensionsv1.JSON{Raw: raw} + } + + // Merge sourceRef into values (overwrite if exists) + valuesMap["sourceRef"] = sourceRefMap + + // Marshal back to JSON + raw, err := json.Marshal(valuesMap) + if err != nil { + // If marshal fails, return original values + return values + } + + return &apiextensionsv1.JSON{Raw: raw} +} + +// getBasePath returns the basePath with default values based on source kind +func (r *PlatformReconciler) getBasePath(platform *cozyv1alpha1.Platform) string { + if platform.Spec.BasePath != "" { + return platform.Spec.BasePath + } + // Default values based on kind + if platform.Spec.SourceRef.Kind == "OCIRepository" { + return "core/platform" // Full path for OCI + } + // Default for GitRepository + return "packages/core/platform" // Full path for Git +} + +// buildSourcePath builds the full source path from basePath and chart path +func (r *PlatformReconciler) buildSourcePath(sourceName, basePath, chartPath string) string { + // Remove leading/trailing slashes and combine + parts := []string{} + if basePath != "" { + parts = append(parts, strings.Trim(basePath, "/")) + } + if chartPath != "" { + parts = append(parts, strings.Trim(chartPath, "/")) + } + fullPath := strings.Join(parts, "/") + if fullPath == "" { + return fmt.Sprintf("@%s", sourceName) + } + return fmt.Sprintf("@%s/%s", sourceName, fullPath) +} + +// cleanupOrphanedResources removes ArtifactGenerator and HelmRelease when Platform is deleted +func (r *PlatformReconciler) cleanupOrphanedResources(ctx context.Context, name client.ObjectKey) (ctrl.Result, error) { + logger := log.FromContext(ctx) + namespace := "cozy-system" + + // Cleanup HelmReleases with the platform label that don't match + hrList := &helmv2.HelmReleaseList{} + if err := r.List(ctx, hrList, client.InNamespace(namespace), client.MatchingLabels{ + "cozystack.io/platform": name.Name, + }); err != nil { + logger.Error(err, "failed to list HelmReleases for cleanup") + return ctrl.Result{}, err + } + + for i := range hrList.Items { + hr := &hrList.Items[i] + // Check if this HelmRelease should exist (matches current Platform name) + // Since Platform is being deleted, all matching HelmReleases should be deleted + // OwnerReferences should handle this, but we'll also delete explicitly + if err := r.Delete(ctx, hr); err != nil { + if !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete orphaned HelmRelease", "name", hr.Name) + } + } else { + logger.Info("deleted orphaned HelmRelease", "name", hr.Name) + } + } + + // Cleanup ArtifactGenerators with the platform label + agList := &sourcewatcherv1beta1.ArtifactGeneratorList{} + if err := r.List(ctx, agList, client.InNamespace(namespace), client.MatchingLabels{ + "cozystack.io/platform": name.Name, + }); err != nil { + logger.Error(err, "failed to list ArtifactGenerators for cleanup") + return ctrl.Result{}, err + } + + for i := range agList.Items { + ag := &agList.Items[i] + if err := r.Delete(ctx, ag); err != nil { + if !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete orphaned ArtifactGenerator", "name", ag.Name) + } + } else { + logger.Info("deleted orphaned ArtifactGenerator", "name", ag.Name) + } + } + + return ctrl.Result{}, nil +} + +// cleanupOrphanedPlatformResources removes HelmRelease and ArtifactGenerator resources +// that have the platform label but don't match the current Platform +func (r *PlatformReconciler) cleanupOrphanedPlatformResources(ctx context.Context, platform *cozyv1alpha1.Platform) error { + logger := log.FromContext(ctx) + namespace := "cozy-system" + platformName := platform.Name + + // Cleanup orphaned HelmReleases + hrList := &helmv2.HelmReleaseList{} + if err := r.List(ctx, hrList, client.InNamespace(namespace), client.MatchingLabels{ + "cozystack.io/platform": platformName, + }); err != nil { + return fmt.Errorf("failed to list HelmReleases: %w", err) + } + + for i := range hrList.Items { + hr := &hrList.Items[i] + // Only delete if it doesn't match the current Platform name + // (in case Platform name changed) + if hr.Name != platformName { + logger.Info("deleting orphaned HelmRelease", "name", hr.Name, "expected", platformName) + if err := r.Delete(ctx, hr); err != nil { + if !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete orphaned HelmRelease", "name", hr.Name) + // Continue with other resources + } + } + } + } + + // Cleanup orphaned ArtifactGenerators + agList := &sourcewatcherv1beta1.ArtifactGeneratorList{} + if err := r.List(ctx, agList, client.InNamespace(namespace), client.MatchingLabels{ + "cozystack.io/platform": platformName, + }); err != nil { + return fmt.Errorf("failed to list ArtifactGenerators: %w", err) + } + + for i := range agList.Items { + ag := &agList.Items[i] + // Only delete if it doesn't match the current Platform name + if ag.Name != platformName { + logger.Info("deleting orphaned ArtifactGenerator", "name", ag.Name, "expected", platformName) + if err := r.Delete(ctx, ag); err != nil { + if !apierrors.IsNotFound(err) { + logger.Error(err, "failed to delete orphaned ArtifactGenerator", "name", ag.Name) + // Continue with other resources + } + } + } + } + + return nil +} + +// createOrUpdate creates or updates a resource +func (r *PlatformReconciler) createOrUpdate(ctx context.Context, obj client.Object) error { + existing := obj.DeepCopyObject().(client.Object) + key := client.ObjectKeyFromObject(obj) + + err := r.Get(ctx, key, existing) + if apierrors.IsNotFound(err) { + return r.Create(ctx, obj) + } else if err != nil { + return err + } + + // Preserve resource version + obj.SetResourceVersion(existing.GetResourceVersion()) + // Merge labels and annotations + labels := obj.GetLabels() + if labels == nil { + labels = make(map[string]string) + } + for k, v := range existing.GetLabels() { + if _, ok := labels[k]; !ok { + labels[k] = v + } + } + obj.SetLabels(labels) + + annotations := obj.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + for k, v := range existing.GetAnnotations() { + if _, ok := annotations[k]; !ok { + annotations[k] = v + } + } + obj.SetAnnotations(annotations) + + // For ArtifactGenerator, explicitly update Spec and ownerReferences + if ag, ok := obj.(*sourcewatcherv1beta1.ArtifactGenerator); ok { + if existingAG, ok := existing.(*sourcewatcherv1beta1.ArtifactGenerator); ok { + logger := log.FromContext(ctx) + logger.V(1).Info("updating ArtifactGenerator Spec", "name", ag.Name, "namespace", ag.Namespace) + existingAG.Spec = ag.Spec + existingAG.SetLabels(ag.GetLabels()) + existingAG.SetAnnotations(ag.GetAnnotations()) + // Always use ownerReferences from the new object (set in reconcileArtifactGenerator) + existingAG.SetOwnerReferences(ag.GetOwnerReferences()) + obj = existingAG + } + } + + // For HelmRelease, explicitly update Spec and ownerReferences + if hr, ok := obj.(*helmv2.HelmRelease); ok { + if existingHR, ok := existing.(*helmv2.HelmRelease); ok { + logger := log.FromContext(ctx) + logger.V(1).Info("updating HelmRelease Spec", "name", hr.Name, "namespace", hr.Namespace) + existingHR.Spec = hr.Spec + existingHR.SetLabels(hr.GetLabels()) + existingHR.SetAnnotations(hr.GetAnnotations()) + // Always use ownerReferences from the new object (set in reconcileHelmRelease) + existingHR.SetOwnerReferences(hr.GetOwnerReferences()) + obj = existingHR + } + } + + return r.Update(ctx, obj) +} + +// SetupWithManager sets up the controller with the Manager +func (r *PlatformReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + Named("cozystack-platform"). + For(&cozyv1alpha1.Platform{}). + Watches( + &helmv2.HelmRelease{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + hr, ok := obj.(*helmv2.HelmRelease) + if !ok { + return nil + } + // Only watch HelmReleases with cozystack.io/platform label + platformName := hr.Labels["cozystack.io/platform"] + if platformName == "" { + return nil + } + return []reconcile.Request{ + { + NamespacedName: client.ObjectKey{ + Name: platformName, + // Cluster-scoped resource has no namespace + }, + }, + } + }), + builder.WithPredicates( + predicate.NewPredicateFuncs(func(obj client.Object) bool { + // Only watch resources with cozystack.io/platform label + labels := obj.GetLabels() + return labels != nil && labels["cozystack.io/platform"] != "" + }), + ), + ). + Watches( + &sourcewatcherv1beta1.ArtifactGenerator{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + ag, ok := obj.(*sourcewatcherv1beta1.ArtifactGenerator) + if !ok { + return nil + } + // Only watch ArtifactGenerators with cozystack.io/platform label + platformName := ag.Labels["cozystack.io/platform"] + if platformName == "" { + return nil + } + return []reconcile.Request{ + { + NamespacedName: client.ObjectKey{ + Name: platformName, + // Cluster-scoped resource has no namespace + }, + }, + } + }), + builder.WithPredicates( + predicate.NewPredicateFuncs(func(obj client.Object) bool { + // Only watch resources with cozystack.io/platform label + labels := obj.GetLabels() + return labels != nil && labels["cozystack.io/platform"] != "" + }), + ), + ). + Complete(r) +} diff --git a/internal/shared/crdmem/memory.go b/internal/shared/crdmem/memory.go index fbfbeeea..f0446627 100644 --- a/internal/shared/crdmem/memory.go +++ b/internal/shared/crdmem/memory.go @@ -11,13 +11,13 @@ import ( type Memory struct { mu sync.RWMutex - data map[string]cozyv1alpha1.CozystackResourceDefinition + data map[string]cozyv1alpha1.ApplicationDefinition primed bool primeOnce sync.Once } func New() *Memory { - return &Memory{data: make(map[string]cozyv1alpha1.CozystackResourceDefinition)} + return &Memory{data: make(map[string]cozyv1alpha1.ApplicationDefinition)} } var ( @@ -30,7 +30,7 @@ func Global() *Memory { return global } -func (m *Memory) Upsert(obj *cozyv1alpha1.CozystackResourceDefinition) { +func (m *Memory) Upsert(obj *cozyv1alpha1.ApplicationDefinition) { if obj == nil { return } @@ -45,10 +45,10 @@ func (m *Memory) Delete(name string) { m.mu.Unlock() } -func (m *Memory) Snapshot() []cozyv1alpha1.CozystackResourceDefinition { +func (m *Memory) Snapshot() []cozyv1alpha1.ApplicationDefinition { m.mu.RLock() defer m.mu.RUnlock() - out := make([]cozyv1alpha1.CozystackResourceDefinition, 0, len(m.data)) + out := make([]cozyv1alpha1.ApplicationDefinition, 0, len(m.data)) for _, v := range m.data { out = append(out, v) } @@ -72,7 +72,7 @@ func (m *Memory) EnsurePrimingWithManager(mgr ctrl.Manager) error { if ok := mgr.GetCache().WaitForCacheSync(ctx); !ok { return nil } - var list cozyv1alpha1.CozystackResourceDefinitionList + var list cozyv1alpha1.ApplicationDefinitionList if err := mgr.GetClient().List(ctx, &list); err == nil { for i := range list.Items { m.Upsert(&list.Items[i]) @@ -87,11 +87,11 @@ func (m *Memory) EnsurePrimingWithManager(mgr ctrl.Manager) error { return errOut } -func (m *Memory) ListFromCacheOrAPI(ctx context.Context, c client.Client) ([]cozyv1alpha1.CozystackResourceDefinition, error) { +func (m *Memory) ListFromCacheOrAPI(ctx context.Context, c client.Client) ([]cozyv1alpha1.ApplicationDefinition, error) { if m.IsPrimed() { return m.Snapshot(), nil } - var list cozyv1alpha1.CozystackResourceDefinitionList + var list cozyv1alpha1.ApplicationDefinitionList if err := c.List(ctx, &list); err != nil { return nil, err } diff --git a/internal/telemetry/collector.go b/internal/telemetry/collector.go index 04d05d3a..494361ce 100644 --- a/internal/telemetry/collector.go +++ b/internal/telemetry/collector.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "net/http" + "sort" "strings" "time" @@ -120,16 +121,50 @@ func (c *Collector) collect(ctx context.Context) { clusterID := string(kubeSystemNS.UID) - var cozystackCM corev1.ConfigMap - if err := c.client.Get(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack"}, &cozystackCM); err != nil { - logger.Info(fmt.Sprintf("Failed to get cozystack configmap in cozy-system namespace: %v", err)) - return - } + // Get all Bundles + var bundleList cozyv1alpha1.BundleList + bundleNameStr := "" + bundleEnable := "" + bundleDisable := "" + oidcEnabled := "false" - oidcEnabled := cozystackCM.Data["oidc-enabled"] - bundle := cozystackCM.Data["bundle-name"] - bundleEnable := cozystackCM.Data["bundle-enable"] - bundleDisable := cozystackCM.Data["bundle-disable"] + if err := c.client.List(ctx, &bundleList); err != nil { + logger.Info(fmt.Sprintf("Failed to list Bundles: %v", err)) + // Continue with empty bundle data instead of returning + } else { + // Collect bundle names (sorted alphabetically) + bundleNames := make([]string, 0, len(bundleList.Items)) + for _, bundle := range bundleList.Items { + bundleNames = append(bundleNames, bundle.Name) + } + sort.Strings(bundleNames) + bundleNameStr = strings.Join(bundleNames, ",") + + // Collect all packages from all bundles + var allEnabledPackages []string + var allDisabledPackages []string + + for _, bundle := range bundleList.Items { + for _, pkg := range bundle.Spec.Packages { + if pkg.Disabled { + allDisabledPackages = append(allDisabledPackages, pkg.Name) + } else { + allEnabledPackages = append(allEnabledPackages, pkg.Name) + // Check if keycloak package is enabled + if pkg.Name == "keycloak" { + oidcEnabled = "true" + } + } + } + } + + // Sort package lists alphabetically + sort.Strings(allEnabledPackages) + sort.Strings(allDisabledPackages) + + bundleEnable = strings.Join(allEnabledPackages, ",") + bundleDisable = strings.Join(allDisabledPackages, ",") + } // Get Kubernetes version from nodes var nodeList corev1.NodeList @@ -143,32 +178,41 @@ func (c *Collector) collect(ctx context.Context) { // Add Cozystack info metric if len(nodeList.Items) > 0 { - k8sVersion, _ := c.discoveryClient.ServerVersion() + k8sVersion := "unknown" + if version, err := c.discoveryClient.ServerVersion(); err == nil && version != nil { + k8sVersion = version.String() + } metrics.WriteString(fmt.Sprintf( - "cozy_cluster_info{cozystack_version=\"%s\",kubernetes_version=\"%s\",oidc_enabled=\"%s\",bundle_name=\"%s\",bunde_enable=\"%s\",bunde_disable=\"%s\"} 1\n", + "cozy_cluster_info{cozystack_version=\"%s\",kubernetes_version=\"%s\",oidc_enabled=\"%s\",bundle_name=\"%s\",bundle_enable=\"%s\",bundle_disable=\"%s\"} 1\n", c.config.CozystackVersion, k8sVersion, oidcEnabled, - bundle, + bundleNameStr, bundleEnable, bundleDisable, )) } // Collect node metrics + if len(nodeList.Items) > 0 { nodeOSCount := make(map[string]int) + kernelVersion := "unknown" for _, node := range nodeList.Items { key := fmt.Sprintf("%s (%s)", node.Status.NodeInfo.OperatingSystem, node.Status.NodeInfo.OSImage) nodeOSCount[key] = nodeOSCount[key] + 1 + if kernelVersion == "unknown" && node.Status.NodeInfo.KernelVersion != "" { + kernelVersion = node.Status.NodeInfo.KernelVersion + } } for osKey, count := range nodeOSCount { metrics.WriteString(fmt.Sprintf( "cozy_nodes_count{os=\"%s\",kernel=\"%s\"} %d\n", osKey, - nodeList.Items[0].Status.NodeInfo.KernelVersion, + kernelVersion, count, )) + } } // Collect LoadBalancer services metrics @@ -248,9 +292,8 @@ func (c *Collector) collect(ctx context.Context) { var monitorList cozyv1alpha1.WorkloadMonitorList if err := c.client.List(ctx, &monitorList); err != nil { logger.Info(fmt.Sprintf("Failed to list WorkloadMonitors: %v", err)) - return - } - + // Continue without workload metrics instead of returning + } else { for _, monitor := range monitorList.Items { metrics.WriteString(fmt.Sprintf( "cozy_workloads_count{uid=\"%s\",kind=\"%s\",type=\"%s\",version=\"%s\"} %d\n", @@ -260,6 +303,7 @@ func (c *Collector) collect(ctx context.Context) { monitor.Spec.Version, monitor.Status.ObservedReplicas, )) + } } // Send metrics diff --git a/packages/apps/Makefile b/packages/apps/Makefile index b3917f20..59e3e61f 100644 --- a/packages/apps/Makefile +++ b/packages/apps/Makefile @@ -1,12 +1,7 @@ OUT=../../_out/repos/apps CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}') -include ../../scripts/common-envs.mk - -repo: - rm -rf "$(OUT)" - helm package -d "$(OUT)" $(CHARTS) --version $(COZYSTACK_VERSION) - helm repo index "$(OUT)" +include ../../hack/common-envs.mk fix-charts: find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}' | while read i; do sed -i -e "s/^name: .*/name: $$i/" -e "s/^version: .*/version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process/g" "$$i/Chart.yaml"; done diff --git a/packages/apps/README.md b/packages/apps/README.md deleted file mode 100644 index 8ee4c3ed..00000000 --- a/packages/apps/README.md +++ /dev/null @@ -1,8 +0,0 @@ -### How to test packages local - -```bash -cd packages/core/installer -make image-cozystack REGISTRY=YOUR_CUSTOM_REGISTRY -make apply -kubectl delete po -l app=source-controller -n cozy-fluxcd -``` diff --git a/packages/apps/bucket/Makefile b/packages/apps/bucket/Makefile index d448d0c0..27cd199b 100644 --- a/packages/apps/bucket/Makefile +++ b/packages/apps/bucket/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/bucket/templates/bucketclaim.yaml b/packages/apps/bucket/templates/bucketclaim.yaml index cebf95b4..1c5df452 100644 --- a/packages/apps/bucket/templates/bucketclaim.yaml +++ b/packages/apps/bucket/templates/bucketclaim.yaml @@ -1,5 +1,6 @@ -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $seaweedfs := index $myNS.metadata.annotations "namespace.cozystack.io/seaweedfs" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $namespace := .Values._namespace | default dict }} +{{- $seaweedfs := dig "seaweedfs" "" $namespace }} apiVersion: objectstorage.k8s.io/v1alpha1 kind: BucketClaim metadata: diff --git a/packages/apps/bucket/templates/helmrelease.yaml b/packages/apps/bucket/templates/helmrelease.yaml index 45959572..0aebdaf7 100644 --- a/packages/apps/bucket/templates/helmrelease.yaml +++ b/packages/apps/bucket/templates/helmrelease.yaml @@ -3,15 +3,10 @@ kind: HelmRelease metadata: name: {{ .Release.Name }}-system spec: - chart: - spec: - chart: cozy-bucket - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-bucket + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/clickhouse/Makefile b/packages/apps/clickhouse/Makefile index 44ac851f..2c77add9 100644 --- a/packages/apps/clickhouse/Makefile +++ b/packages/apps/clickhouse/Makefile @@ -1,7 +1,7 @@ CLICKHOUSE_BACKUP_TAG = $(shell awk '$$0 ~ /^version:/ {print $$2}' Chart.yaml) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/clickhouse/templates/chkeeper.yaml b/packages/apps/clickhouse/templates/chkeeper.yaml index 54824a44..2b6602b4 100644 --- a/packages/apps/clickhouse/templates/chkeeper.yaml +++ b/packages/apps/clickhouse/templates/chkeeper.yaml @@ -1,5 +1,5 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $clusterDomain := dig "networking" "clusterDomain" "cozy.local" $cozystack }} {{- if .Values.clickhouseKeeper.enabled }} apiVersion: "clickhouse-keeper.altinity.com/v1" diff --git a/packages/apps/clickhouse/templates/clickhouse.yaml b/packages/apps/clickhouse/templates/clickhouse.yaml index 47e5b56a..86e5a699 100644 --- a/packages/apps/clickhouse/templates/clickhouse.yaml +++ b/packages/apps/clickhouse/templates/clickhouse.yaml @@ -1,5 +1,5 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $clusterDomain := dig "networking" "clusterDomain" "cozy.local" $cozystack }} {{- $existingSecret := lookup "v1" "Secret" .Release.Namespace (printf "%s-credentials" .Release.Name) }} {{- $passwords := dict }} {{- $users := .Values.users }} diff --git a/packages/apps/ferretdb/Makefile b/packages/apps/ferretdb/Makefile index bd52fb05..40b7423f 100644 --- a/packages/apps/ferretdb/Makefile +++ b/packages/apps/ferretdb/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/ferretdb/templates/postgres.yaml b/packages/apps/ferretdb/templates/postgres.yaml index 4d1d8e29..7712cb97 100644 --- a/packages/apps/ferretdb/templates/postgres.yaml +++ b/packages/apps/ferretdb/templates/postgres.yaml @@ -50,15 +50,13 @@ spec: postgresUID: 999 postgresGID: 999 enableSuperuserAccess: true - {{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} - {{- if $configMap }} - {{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }} - {{- if $rawConstraints }} - {{- $rawConstraints | fromYaml | toYaml | nindent 2 }} - labelSelector: - matchLabels: - cnpg.io/cluster: {{ .Release.Name }}-postgres - {{- end }} + {{- $cozystack := .Values._cozystack | default dict }} + {{- $topologySpreadConstraints := dig "scheduling" "topologySpreadConstraints" (list) $cozystack }} + {{- if $topologySpreadConstraints }} + topologySpreadConstraints: + {{- range $topologySpreadConstraints }} + - {{- mergeOverwrite (dict "labelSelector" (dict "matchLabels" (dict "cnpg.io/cluster" (printf "%s-postgres" $.Release.Name)))) . | toYaml | nindent 6 | trim }} + {{- end }} {{- end }} minSyncReplicas: {{ .Values.quorum.minSyncReplicas }} maxSyncReplicas: {{ .Values.quorum.maxSyncReplicas }} diff --git a/packages/apps/foundationdb/Makefile b/packages/apps/foundationdb/Makefile index b885e4b1..76c84980 100644 --- a/packages/apps/foundationdb/Makefile +++ b/packages/apps/foundationdb/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md \ No newline at end of file diff --git a/packages/apps/foundationdb/templates/cluster.yaml b/packages/apps/foundationdb/templates/cluster.yaml index 06992cb5..56bdb812 100644 --- a/packages/apps/foundationdb/templates/cluster.yaml +++ b/packages/apps/foundationdb/templates/cluster.yaml @@ -1,5 +1,5 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" | default (dict "data" (dict)) }} -{{- $clusterDomain := index $cozyConfig.data "cluster-domain" | default "cozy.local" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $clusterDomain := dig "networking" "clusterDomain" "cozy.local" $cozystack }} --- apiVersion: apps.foundationdb.org/v1beta2 kind: FoundationDBCluster diff --git a/packages/apps/http-cache/Makefile b/packages/apps/http-cache/Makefile index 3237f634..2cc11f87 100644 --- a/packages/apps/http-cache/Makefile +++ b/packages/apps/http-cache/Makefile @@ -1,7 +1,7 @@ NGINX_CACHE_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: image-nginx diff --git a/packages/apps/kafka/Makefile b/packages/apps/kafka/Makefile index 0d71076e..2cffdfa7 100644 --- a/packages/apps/kafka/Makefile +++ b/packages/apps/kafka/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"] generate: diff --git a/packages/apps/kubernetes/Makefile b/packages/apps/kubernetes/Makefile index 799a19bc..198929b1 100644 --- a/packages/apps/kubernetes/Makefile +++ b/packages/apps/kubernetes/Makefile @@ -1,8 +1,8 @@ KUBERNETES_VERSION = v1.33 KUBERNETES_PKG_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/kubernetes/templates/cluster.yaml b/packages/apps/kubernetes/templates/cluster.yaml index 7edd07f5..28c601fe 100644 --- a/packages/apps/kubernetes/templates/cluster.yaml +++ b/packages/apps/kubernetes/templates/cluster.yaml @@ -1,7 +1,8 @@ -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $etcd := index $myNS.metadata.annotations "namespace.cozystack.io/etcd" }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $namespace := .Values._namespace | default dict }} +{{- $etcd := dig "etcd" "" $namespace }} +{{- $ingress := dig "ingress" "" $namespace }} +{{- $host := dig "host" "" $namespace }} {{- $kubevirtmachinetemplateNames := list }} {{- define "kubevirtmachinetemplate" -}} spec: @@ -31,14 +32,11 @@ spec: {{- end }} cluster.x-k8s.io/deployment-name: {{ $.Release.Name }}-{{ .groupName }} spec: - {{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} - {{- if $configMap }} - {{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }} - {{- if $rawConstraints }} - {{- $rawConstraints | fromYaml | toYaml | nindent 10 }} - labelSelector: - matchLabels: - cluster.x-k8s.io/cluster-name: {{ $.Release.Name }} + {{- $cozystack := $.Values._cozystack | default dict }} + {{- $topologySpreadConstraints := dig "scheduling" "topologySpreadConstraints" (list) $cozystack }} + {{- if $topologySpreadConstraints }} + {{- range $topologySpreadConstraints }} + - {{- mergeOverwrite (dict "labelSelector" (dict "matchLabels" (dict "cluster.x-k8s.io/cluster-name" $.Release.Name))) . | toYaml | nindent 12 | trim }} {{- end }} {{- end }} domain: diff --git a/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml index 3bf7a5d2..78fc1a6b 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml @@ -8,15 +8,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: cert-manager-crds - chart: - spec: - chart: cozy-cert-manager-crds - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-cert-manager-crds + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml b/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml index e0caf4cb..d82eab88 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml @@ -8,15 +8,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: cert-manager - chart: - spec: - chart: cozy-cert-manager - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-cert-manager + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/cilium.yaml b/packages/apps/kubernetes/templates/helmreleases/cilium.yaml index c356dc79..e9ca83d5 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cilium.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cilium.yaml @@ -22,15 +22,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: cilium - chart: - spec: - chart: cozy-cilium - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-cilium + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/coredns.yaml b/packages/apps/kubernetes/templates/helmreleases/coredns.yaml index 37a09a0b..4b5e1334 100644 --- a/packages/apps/kubernetes/templates/helmreleases/coredns.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/coredns.yaml @@ -13,15 +13,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: coredns - chart: - spec: - chart: cozy-coredns - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-coredns + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/csi.yaml b/packages/apps/kubernetes/templates/helmreleases/csi.yaml index 09927e8a..d4c58699 100644 --- a/packages/apps/kubernetes/templates/helmreleases/csi.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/csi.yaml @@ -8,15 +8,10 @@ metadata: spec: interval: 5m releaseName: csi - chart: - spec: - chart: cozy-kubevirt-csi-node - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-kubevirt-csi-node + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml b/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml index 7518601b..5f179394 100644 --- a/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml @@ -8,15 +8,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: fluxcd-operator - chart: - spec: - chart: cozy-fluxcd-operator - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-fluxcd-operator + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig @@ -56,15 +51,10 @@ metadata: spec: interval: 5m releaseName: fluxcd - chart: - spec: - chart: cozy-fluxcd - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-fluxcd + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml index 48a20c5a..e6c62765 100644 --- a/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml @@ -8,15 +8,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: gateway-api-crds - chart: - spec: - chart: cozy-gateway-api-crds - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-gateway-api-crds + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml b/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml index fbee1724..9c0df29c 100644 --- a/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml @@ -8,15 +8,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: gpu-operator - chart: - spec: - chart: cozy-gpu-operator - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-gpu-operator + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml b/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml index f80dd7ae..17120e8f 100644 --- a/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml @@ -27,15 +27,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: ingress-nginx - chart: - spec: - chart: cozy-ingress-nginx - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-ingress-nginx + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml b/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml index 8f914b08..5bfe9b89 100644 --- a/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml @@ -1,5 +1,6 @@ -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $targetTenant := index $myNS.metadata.annotations "namespace.cozystack.io/monitoring" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $namespace := .Values._namespace | default dict }} +{{- $targetTenant := dig "monitoring" "" $namespace }} {{- if .Values.addons.monitoringAgents.enabled }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease @@ -10,15 +11,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: cozy-monitoring-agents - chart: - spec: - chart: cozy-monitoring-agents - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-monitoring-agents + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/velero.yaml b/packages/apps/kubernetes/templates/helmreleases/velero.yaml index 0c918da6..dd3023f1 100644 --- a/packages/apps/kubernetes/templates/helmreleases/velero.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/velero.yaml @@ -8,15 +8,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: velero - chart: - spec: - chart: cozy-velero - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-velero + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml index 336c198b..543dc693 100644 --- a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml @@ -9,15 +9,10 @@ metadata: spec: interval: 5m releaseName: vertical-pod-autoscaler-crds - chart: - spec: - chart: cozy-vertical-pod-autoscaler-crds - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-vertical-pod-autoscaler-crds + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml index 8b615c9c..82361057 100644 --- a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml @@ -1,8 +1,8 @@ {{- define "cozystack.defaultVPAValues" -}} -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $targetTenant := index $myNS.metadata.annotations "namespace.cozystack.io/monitoring" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $clusterDomain := dig "networking" "clusterDomain" "cozy.local" $cozystack }} +{{- $namespace := .Values._namespace | default dict }} +{{- $targetTenant := dig "monitoring" "" $namespace }} vpaForVPA: false vertical-pod-autoscaler: recommender: @@ -36,15 +36,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: vertical-pod-autoscaler - chart: - spec: - chart: cozy-vertical-pod-autoscaler - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-vertical-pod-autoscaler + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml b/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml index dbb4d8dc..367cbdb0 100644 --- a/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml @@ -8,15 +8,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: cozy-victoria-metrics-operator - chart: - spec: - chart: cozy-victoria-metrics-operator - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-victoria-metrics-operator + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/volumesnapshot_crd.yaml b/packages/apps/kubernetes/templates/helmreleases/volumesnapshot_crd.yaml index 97cf06ad..31062608 100644 --- a/packages/apps/kubernetes/templates/helmreleases/volumesnapshot_crd.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/volumesnapshot_crd.yaml @@ -7,15 +7,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: vsnap-crd - chart: - spec: - chart: cozy-vsnap-crd - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-iaas-kubernetes-volumesnapshot-crd + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/ingress.yaml b/packages/apps/kubernetes/templates/ingress.yaml index 8dd244cb..84df0300 100644 --- a/packages/apps/kubernetes/templates/ingress.yaml +++ b/packages/apps/kubernetes/templates/ingress.yaml @@ -1,5 +1,6 @@ -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $namespace := .Values._namespace | default dict }} +{{- $ingress := dig "ingress" "" $namespace }} {{- if and (eq .Values.addons.ingressNginx.exposeMethod "Proxied") .Values.addons.ingressNginx.hosts }} --- apiVersion: networking.k8s.io/v1 diff --git a/packages/apps/mysql/Makefile b/packages/apps/mysql/Makefile index 08c57889..ba94d1b8 100644 --- a/packages/apps/mysql/Makefile +++ b/packages/apps/mysql/Makefile @@ -1,7 +1,7 @@ MARIADB_BACKUP_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/nats/Makefile b/packages/apps/nats/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/nats/Makefile +++ b/packages/apps/nats/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/nats/templates/nats.yaml b/packages/apps/nats/templates/nats.yaml index b05c87b5..da2a708f 100644 --- a/packages/apps/nats/templates/nats.yaml +++ b/packages/apps/nats/templates/nats.yaml @@ -1,5 +1,5 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $clusterDomain := dig "networking" "clusterDomain" "cozy.local" $cozystack }} {{- $existingSecret := lookup "v1" "Secret" .Release.Namespace (printf "%s-credentials" .Release.Name) }} {{- $passwords := dict }} @@ -35,15 +35,10 @@ kind: HelmRelease metadata: name: {{ .Release.Name }}-system spec: - chart: - spec: - chart: cozy-nats - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-paas-nats + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/postgres/Makefile b/packages/apps/postgres/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/postgres/Makefile +++ b/packages/apps/postgres/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/postgres/templates/db.yaml b/packages/apps/postgres/templates/db.yaml index de4f51a3..233529d1 100644 --- a/packages/apps/postgres/templates/db.yaml +++ b/packages/apps/postgres/templates/db.yaml @@ -45,15 +45,13 @@ spec: resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 4 }} enableSuperuserAccess: true - {{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} - {{- if $configMap }} - {{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }} - {{- if $rawConstraints }} - {{- $rawConstraints | fromYaml | toYaml | nindent 2 }} - labelSelector: - matchLabels: - cnpg.io/cluster: {{ .Release.Name }} - {{- end }} + {{- $cozystack := .Values._cozystack | default dict }} + {{- $topologySpreadConstraints := dig "scheduling" "topologySpreadConstraints" (list) $cozystack }} + {{- if $topologySpreadConstraints }} + topologySpreadConstraints: + {{- range $topologySpreadConstraints }} + - {{- mergeOverwrite (dict "labelSelector" (dict "matchLabels" (dict "cnpg.io/cluster" $.Release.Name))) . | toYaml | nindent 6 | trim }} + {{- end }} {{- end }} postgresql: parameters: diff --git a/packages/apps/rabbitmq/Makefile b/packages/apps/rabbitmq/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/rabbitmq/Makefile +++ b/packages/apps/rabbitmq/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/redis/Makefile b/packages/apps/redis/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/redis/Makefile +++ b/packages/apps/redis/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/tcp-balancer/Makefile b/packages/apps/tcp-balancer/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/tcp-balancer/Makefile +++ b/packages/apps/tcp-balancer/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/tenant/Makefile b/packages/apps/tenant/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/tenant/Makefile +++ b/packages/apps/tenant/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/tenant/templates/etcd.yaml b/packages/apps/tenant/templates/etcd.yaml index 9db6ff6c..05431fb9 100644 --- a/packages/apps/tenant/templates/etcd.yaml +++ b/packages/apps/tenant/templates/etcd.yaml @@ -9,16 +9,14 @@ metadata: internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + apps.cozystack.io/application.kind: Etcd + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: etcd spec: - chart: - spec: - chart: etcd - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-etcd + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/info.yaml b/packages/apps/tenant/templates/info.yaml index 4b6f2640..41c0555b 100644 --- a/packages/apps/tenant/templates/info.yaml +++ b/packages/apps/tenant/templates/info.yaml @@ -8,16 +8,14 @@ metadata: internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + apps.cozystack.io/application.kind: Info + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: info spec: - chart: - spec: - chart: info - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-info + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/ingress.yaml b/packages/apps/tenant/templates/ingress.yaml index b866300f..a620685a 100644 --- a/packages/apps/tenant/templates/ingress.yaml +++ b/packages/apps/tenant/templates/ingress.yaml @@ -9,16 +9,14 @@ metadata: internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + apps.cozystack.io/application.kind: Ingress + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: ingress spec: - chart: - spec: - chart: ingress - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-ingress + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/keycloakgroups.yaml b/packages/apps/tenant/templates/keycloakgroups.yaml index 59288ca4..53dbaac4 100644 --- a/packages/apps/tenant/templates/keycloakgroups.yaml +++ b/packages/apps/tenant/templates/keycloakgroups.yaml @@ -1,6 +1,8 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $oidcEnabled := index $cozyConfig.data "oidc-enabled" }} -{{- if eq $oidcEnabled "true" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $authentication := $cozystack.authentication | default dict }} +{{- $oidc := $authentication.oidc | default dict }} +{{- $oidcEnabled := $oidc.enabled | default false }} +{{- if $oidcEnabled }} {{- if .Capabilities.APIVersions.Has "v1.edp.epam.com/v1" }} apiVersion: v1.edp.epam.com/v1 kind: KeycloakRealmGroup diff --git a/packages/apps/tenant/templates/monitoring.yaml b/packages/apps/tenant/templates/monitoring.yaml index edcc66d9..aa1e1172 100644 --- a/packages/apps/tenant/templates/monitoring.yaml +++ b/packages/apps/tenant/templates/monitoring.yaml @@ -9,16 +9,14 @@ metadata: internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + apps.cozystack.io/application.kind: Monitoring + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: monitoring spec: - chart: - spec: - chart: monitoring - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-monitoring + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/namespace.yaml b/packages/apps/tenant/templates/namespace.yaml index d97ebf42..7533e0c8 100644 --- a/packages/apps/tenant/templates/namespace.yaml +++ b/packages/apps/tenant/templates/namespace.yaml @@ -1,15 +1,21 @@ {{- define "cozystack.namespace-anotations" }} {{- $context := index . 0 }} -{{- $existingNS := index . 1 }} +{{- $namespace := index . 1 }} {{- range $x := list "etcd" "monitoring" "ingress" "seaweedfs" }} {{- if (index $context.Values $x) }} namespace.cozystack.io/{{ $x }}: "{{ include "tenant.name" $context }}" {{- else }} -namespace.cozystack.io/{{ $x }}: "{{ index $existingNS.metadata.annotations (printf "namespace.cozystack.io/%s" $x) | required (printf "namespace %s has no namespace.cozystack.io/%s annotation" $context.Release.Namespace $x) }}" +{{- $value := dig $x "" $namespace }} +{{- if $value }} +namespace.cozystack.io/{{ $x }}: "{{ $value }}" +{{- else }} +{{- fail (printf "namespace %s has no namespace.cozystack.io/%s in values.namespace" $context.Release.Namespace $x) }} +{{- end }} {{- end }} {{- end }} {{- end }} +{{- $namespace := .Values._namespace | default dict }} {{- $existingNS := lookup "v1" "Namespace" "" .Release.Namespace }} {{- if not $existingNS }} {{- fail (printf "error lookup existing namespace: %s" .Release.Namespace) }} @@ -26,10 +32,13 @@ metadata: {{- if .Values.host }} namespace.cozystack.io/host: "{{ .Values.host }}" {{- else }} - {{ $parentHost := index $existingNS.metadata.annotations "namespace.cozystack.io/host" | required (printf "namespace %s has no namespace.cozystack.io/host annotation" .Release.Namespace) }} + {{- $parentHost := dig "host" "" $namespace }} + {{- if not $parentHost }} + {{- fail (printf "namespace %s has no host in values.namespace" .Release.Namespace) }} + {{- end }} namespace.cozystack.io/host: "{{ splitList "-" (include "tenant.name" .) | last }}.{{ $parentHost }}" {{- end }} - {{- include "cozystack.namespace-anotations" (list . $existingNS) | nindent 4 }} + {{- include "cozystack.namespace-anotations" (list . $namespace) | nindent 4 }} labels: tenant.cozystack.io/{{ include "tenant.name" $ }}: "" {{- if hasPrefix "tenant-" .Release.Namespace }} @@ -40,7 +49,7 @@ metadata: {{- end }} {{- end }} {{- end }} - {{- include "cozystack.namespace-anotations" (list $ $existingNS) | nindent 4 }} + {{- include "cozystack.namespace-anotations" (list $ $namespace) | nindent 4 }} alpha.kubevirt.io/auto-memory-limits-ratio: "1.0" ownerReferences: - apiVersion: v1 diff --git a/packages/apps/tenant/templates/seaweedfs.yaml b/packages/apps/tenant/templates/seaweedfs.yaml index 936e294b..44c0a66b 100644 --- a/packages/apps/tenant/templates/seaweedfs.yaml +++ b/packages/apps/tenant/templates/seaweedfs.yaml @@ -9,16 +9,14 @@ metadata: internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} + apps.cozystack.io/application.kind: SeaweedFS + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: seaweedfs spec: - chart: - spec: - chart: seaweedfs - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-seaweedfs + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/tenant.yaml b/packages/apps/tenant/templates/tenant.yaml index 6d325a07..06d70eae 100644 --- a/packages/apps/tenant/templates/tenant.yaml +++ b/packages/apps/tenant/templates/tenant.yaml @@ -385,7 +385,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: {{ include "tenant.name" . }} - namespace: cozy-public + namespace: cozy-system rules: - apiGroups: ["source.toolkit.fluxcd.io"] resources: ["helmrepositories"] @@ -398,7 +398,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: {{ include "tenant.name" . }} - namespace: cozy-public + namespace: cozy-system subjects: - kind: Group name: {{ include "tenant.name" . }}-super-admin diff --git a/packages/apps/virtual-machine/Makefile b/packages/apps/virtual-machine/Makefile index b70ac622..7c482228 100644 --- a/packages/apps/virtual-machine/Makefile +++ b/packages/apps/virtual-machine/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/virtual-machine/templates/vm.yaml b/packages/apps/virtual-machine/templates/vm.yaml index 92084acb..acaba6c6 100644 --- a/packages/apps/virtual-machine/templates/vm.yaml +++ b/packages/apps/virtual-machine/templates/vm.yaml @@ -43,7 +43,7 @@ spec: {{- if $dv }} pvc: name: vm-image-{{ .Values.systemDisk.image }} - namespace: cozy-public + namespace: cozy-system {{- else }} http: {{- if eq .Values.systemDisk.image "cirros" }} diff --git a/packages/apps/vm-disk/Makefile b/packages/apps/vm-disk/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/vm-disk/Makefile +++ b/packages/apps/vm-disk/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/vm-instance/Makefile b/packages/apps/vm-instance/Makefile index 105ca7db..3b6d471d 100644 --- a/packages/apps/vm-instance/Makefile +++ b/packages/apps/vm-instance/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/vpc/Makefile b/packages/apps/vpc/Makefile index 8807454f..ea4f5d1b 100644 --- a/packages/apps/vpc/Makefile +++ b/packages/apps/vpc/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json diff --git a/packages/apps/vpn/Makefile b/packages/apps/vpn/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/vpn/Makefile +++ b/packages/apps/vpn/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/vpn/templates/secret.yaml b/packages/apps/vpn/templates/secret.yaml index 79960096..d97d2a24 100644 --- a/packages/apps/vpn/templates/secret.yaml +++ b/packages/apps/vpn/templates/secret.yaml @@ -1,5 +1,6 @@ -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $namespace := .Values._namespace | default dict }} +{{- $host := dig "host" "" $namespace }} {{- $existingSecret := lookup "v1" "Secret" .Release.Namespace (printf "%s-vpn" .Release.Name) }} {{- $accessKeys := list }} {{- $passwords := dict }} diff --git a/packages/system/cozystack-resource-definitions/Chart.yaml b/packages/core/flux-aio/Chart.yaml similarity index 75% rename from packages/system/cozystack-resource-definitions/Chart.yaml rename to packages/core/flux-aio/Chart.yaml index 39cb9431..2e625a16 100644 --- a/packages/system/cozystack-resource-definitions/Chart.yaml +++ b/packages/core/flux-aio/Chart.yaml @@ -1,3 +1,3 @@ apiVersion: v2 -name: cozystack-resource-definitions +name: cozy-fluxcd version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/core/flux-aio/Makefile b/packages/core/flux-aio/Makefile new file mode 100644 index 00000000..29024c79 --- /dev/null +++ b/packages/core/flux-aio/Makefile @@ -0,0 +1,16 @@ +NAME=flux-aio +NAMESPACE=cozy-$(NAME) + +show: + kubectl apply -R -f manifests/ --dry-run=client -o yaml + +apply: + kubectl apply -R -f manifests/ --server-side --force-conflicts + +diff: + kubectl diff -R -f manifests/ + +update: + timoni bundle build -f flux-aio.cue > manifests/fluxcd.yaml + sed -e 's|\.cluster\.local\.,||g' -e 's|\.cluster\.local\,||g' -e 's|\.cluster\.local\.||g' -e '/timoni/d' -i manifests/fluxcd.yaml + yq eval '(select(.kind == "Namespace") | .metadata.labels."pod-security.kubernetes.io/enforce") = "privileged"' -i manifests/fluxcd.yaml diff --git a/packages/core/flux-aio/flux-aio.cue b/packages/core/flux-aio/flux-aio.cue new file mode 100644 index 00000000..8c067c3b --- /dev/null +++ b/packages/core/flux-aio/flux-aio.cue @@ -0,0 +1,16 @@ +bundle: { + apiVersion: "v1alpha1" + name: "flux-aio" + instances: { + "flux": { + module: { + url: "oci://ghcr.io/stefanprodan/modules/flux-aio" + version: "latest" + } + namespace: "cozy-fluxcd" + values: { + securityProfile: "privileged" + } + } + } +} diff --git a/packages/core/flux-aio/manifests b/packages/core/flux-aio/manifests new file mode 120000 index 00000000..f9bb5aad --- /dev/null +++ b/packages/core/flux-aio/manifests @@ -0,0 +1 @@ +../../../internal/fluxinstall/manifests \ No newline at end of file diff --git a/packages/core/flux-aio/values.yaml b/packages/core/flux-aio/values.yaml new file mode 100644 index 00000000..e269b1fb --- /dev/null +++ b/packages/core/flux-aio/values.yaml @@ -0,0 +1,55 @@ +flux-instance: + instance: + cluster: + networkPolicy: false # -- disable due to liveness/readiness probes issues with network policies + domain: cozy.local # -- default value is overriden in patches + distribution: + artifact: "" + version: 2.7.x + registry: ghcr.io/fluxcd + components: + - source-controller + - source-watcher + - kustomize-controller + - helm-controller + - notification-controller + - image-reflector-controller + - image-automation-controller + kustomize: + patches: + - target: + kind: Deployment + name: "(kustomize-controller|helm-controller|source-controller)" + patch: | + - op: add + path: /spec/template/spec/containers/0/args/- + value: --concurrent=20 + - op: add + path: /spec/template/spec/containers/0/args/- + value: --requeue-dependency=5s + - op: replace + path: /spec/template/spec/containers/0/resources/limits + value: + cpu: 2000m + memory: 2048Mi + - target: + kind: Deployment + name: source-controller + patch: | + - op: add + path: /spec/template/spec/containers/0/args/- + value: --storage-adv-addr=source-controller.cozy-fluxcd.svc + - target: + kind: Deployment + name: source-watcher + patch: | + - op: add + path: /spec/template/spec/containers/0/args/- + value: --storage-adv-addr=source-watcher.cozy-fluxcd.svc + - target: + kind: Deployment + name: (kustomize-controller|helm-controller|image-reflector-controller|image-automation-controller|source-controller|source-watcher) + patch: | + - op: add + path: /spec/template/spec/containers/0/args/- + value: --events-addr=http://notification-controller.cozy-fluxcd.svc/ diff --git a/packages/core/installer/Chart.yaml b/packages/core/installer/Chart.yaml index 91350467..61cf32e6 100644 --- a/packages/core/installer/Chart.yaml +++ b/packages/core/installer/Chart.yaml @@ -1,3 +1,3 @@ apiVersion: v2 -name: cozy-installer +name: cozy-cozystack-operator version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/core/installer/Makefile b/packages/core/installer/Makefile index a51e7b61..0dc0f0c5 100644 --- a/packages/core/installer/Makefile +++ b/packages/core/installer/Makefile @@ -1,9 +1,9 @@ -NAME=installer +NAME=cozystack-operator NAMESPACE=cozy-system -TALOS_VERSION=$(shell awk '/^version:/ {print $$2}' images/talos/profiles/installer.yaml) +include ../../../hack/common-envs.mk -include ../../../scripts/common-envs.mk +image: pre-checks image-operator image-packages update-version pre-checks: ../../../hack/pre-checks.sh @@ -17,44 +17,30 @@ apply: diff: cozypkg show -n $(NAMESPACE) $(NAME) --plain | kubectl diff -f - -update: - hack/gen-profiles.sh - -image: pre-checks image-matchbox image-cozystack image-talos - -image-cozystack: - docker buildx build -f images/cozystack/Dockerfile ../../.. \ - --tag $(REGISTRY)/installer:$(call settag,$(TAG)) \ - --cache-from type=registry,ref=$(REGISTRY)/installer:latest \ +image-operator: + docker buildx build -f images/cozystack-operator/Dockerfile ../../.. \ + --tag $(REGISTRY)/cozystack-operator:$(call settag,$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/cozystack-operator:latest \ --cache-to type=inline \ - --metadata-file images/installer.json \ + --metadata-file images/cozystack-operator.json \ $(BUILDX_ARGS) - IMAGE="$(REGISTRY)/installer:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/installer.json -o json -r)" \ - yq -i '.cozystack.image = strenv(IMAGE)' values.yaml - rm -f images/installer.json + IMAGE="$(REGISTRY)/cozystack-operator:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/cozystack-operator.json -o json -r)" \ + yq -i '.cozystackOperator.image = strenv(IMAGE)' values.yaml + rm -f images/cozystack-operator.json -image-talos: - test -f ../../../_out/assets/installer-amd64.tar || make talos-installer - skopeo copy docker-archive:../../../_out/assets/installer-amd64.tar docker://$(REGISTRY)/talos:$(call settag,$(TALOS_VERSION)) +update-version: + TAG="$(call settag,$(TAG))" \ + yq -i '.cozystackOperator.cozystackVersion = strenv(TAG)' values.yaml -image-matchbox: - test -f ../../../_out/assets/kernel-amd64 || make talos-kernel - test -f ../../../_out/assets/initramfs-metal-amd64.xz || make talos-initramfs - docker buildx build -f images/matchbox/Dockerfile ../../.. \ - --tag $(REGISTRY)/matchbox:$(call settag,$(TAG)) \ - --tag $(REGISTRY)/matchbox:$(call settag,$(TALOS_VERSION)-$(TAG)) \ - --cache-from type=registry,ref=$(REGISTRY)/matchbox:latest \ - --cache-to type=inline \ - --metadata-file images/matchbox.json \ - $(BUILDX_ARGS) - echo "$(REGISTRY)/matchbox:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/matchbox.json -o json -r)" \ - > ../../extra/bootbox/images/matchbox.tag - rm -f images/matchbox.json - -assets: talos-iso talos-nocloud talos-metal talos-kernel talos-initramfs - -talos-initramfs talos-kernel talos-installer talos-iso talos-nocloud talos-metal: - mkdir -p ../../../_out/assets - cat images/talos/profiles/$(subst talos-,,$@).yaml | \ - docker run --rm -i -v /dev:/dev --privileged "ghcr.io/siderolabs/imager:$(TALOS_VERSION)" --tar-to-stdout - | \ - tar -C ../../../_out/assets -xzf- +image-packages: + mkdir -p ../../../_out/assets images + flux push artifact \ + oci://$(REGISTRY)/platform-packages:$(call settag,$(TAG)) \ + --path=../../../packages \ + --source=https://github.com/cozystack/cozystack \ + --revision="$$(git describe --tags):$$(git rev-parse HEAD)" \ + 2>&1 | tee images/cozystack-packages.log + REPO="oci://$(REGISTRY)/platform-packages" \ + yq -i '.cozystackOperator.packagesRepository = strenv(REPO)' values.yaml + export DIGEST=$$(awk -F@ '/artifact successfully pushed/ {print $$2}' images/cozystack-packages.log; rm -f images/cozystack-packages.log); \ + test -n "$$DIGEST" && yq -i '.cozystackOperator.packagesDigest = strenv(DIGEST)' values.yaml diff --git a/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml b/packages/core/installer/crds/cozystack.io_applicationdefinitions.yaml similarity index 92% rename from packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml rename to packages/core/installer/crds/cozystack.io_applicationdefinitions.yaml index 685d4ac5..0aa8f7aa 100644 --- a/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml +++ b/packages/core/installer/crds/cozystack.io_applicationdefinitions.yaml @@ -4,20 +4,22 @@ kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.16.4 - name: cozystackresourcedefinitions.cozystack.io + name: applicationdefinitions.cozystack.io spec: group: cozystack.io names: - kind: CozystackResourceDefinition - listKind: CozystackResourceDefinitionList - plural: cozystackresourcedefinitions - singular: cozystackresourcedefinition + kind: ApplicationDefinition + listKind: ApplicationDefinitionList + plural: applicationdefinitions + shortNames: + - appdef + singular: applicationdefinition scope: Cluster versions: - name: v1alpha1 schema: openAPIV3Schema: - description: CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions + description: ApplicationDefinition is the Schema for the applicationdefinitions API properties: apiVersion: @@ -136,7 +138,7 @@ spec: hidden from the user, regardless of the matches in the include array. items: description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. + ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. A resource matches this selector only if it satisfies ALL criteria: - Label selector conditions (matchExpressions and matchLabels) - AND has a name that matches one of the names in resourceNames (if specified) @@ -218,7 +220,7 @@ spec: as a tenant resource and is visible to users. items: description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. + ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. A resource matches this selector only if it satisfies ALL criteria: - Label selector conditions (matchExpressions and matchLabels) - AND has a name that matches one of the names in resourceNames (if specified) @@ -297,7 +299,7 @@ spec: description: Release configuration properties: chart: - description: Helm chart configuration + description: Helm chart configuration (for HelmRepository source) properties: name: description: Name of the Helm chart @@ -325,6 +327,32 @@ spec: - name - sourceRef type: object + chartRef: + description: Chart reference configuration (for ExternalArtifact + source) + properties: + sourceRef: + description: Source reference for the chart (ExternalArtifact) + properties: + kind: + default: HelmRepository + description: Kind of the source reference + type: string + name: + description: Name of the source reference + type: string + namespace: + default: cozy-public + description: Namespace of the source reference + type: string + required: + - kind + - name + - namespace + type: object + required: + - sourceRef + type: object labels: additionalProperties: type: string @@ -333,10 +361,18 @@ spec: prefix: description: Prefix for the release name type: string + values: + description: |- + Default values to be merged into every HelmRelease created from this resource definition + User-specified values in Application spec will override these default values + x-kubernetes-preserve-unknown-fields: true required: - - chart - prefix type: object + x-kubernetes-validations: + - message: either chart or chartRef must be set, but not both + rule: (has(self.chart) && !has(self.chartRef)) || (!has(self.chart) + && has(self.chartRef)) secrets: description: Secret selectors properties: @@ -347,7 +383,7 @@ spec: hidden from the user, regardless of the matches in the include array. items: description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. + ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. A resource matches this selector only if it satisfies ALL criteria: - Label selector conditions (matchExpressions and matchLabels) - AND has a name that matches one of the names in resourceNames (if specified) @@ -429,7 +465,7 @@ spec: as a tenant resource and is visible to users. items: description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. + ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. A resource matches this selector only if it satisfies ALL criteria: - Label selector conditions (matchExpressions and matchLabels) - AND has a name that matches one of the names in resourceNames (if specified) @@ -514,7 +550,7 @@ spec: hidden from the user, regardless of the matches in the include array. items: description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. + ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. A resource matches this selector only if it satisfies ALL criteria: - Label selector conditions (matchExpressions and matchLabels) - AND has a name that matches one of the names in resourceNames (if specified) @@ -596,7 +632,7 @@ spec: as a tenant resource and is visible to users. items: description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. + ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. A resource matches this selector only if it satisfies ALL criteria: - Label selector conditions (matchExpressions and matchLabels) - AND has a name that matches one of the names in resourceNames (if specified) diff --git a/packages/core/installer/crds/cozystack.io_bundles.yaml b/packages/core/installer/crds/cozystack.io_bundles.yaml new file mode 100644 index 00000000..51c7e360 --- /dev/null +++ b/packages/core/installer/crds/cozystack.io_bundles.yaml @@ -0,0 +1,263 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: bundles.cozystack.io +spec: + group: cozystack.io + names: + kind: Bundle + listKind: BundleList + plural: bundles + shortNames: + - bundle + singular: bundle + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Bundle is the Schema for the bundles API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BundleSpec defines the desired state of Bundle + properties: + artifacts: + description: |- + Artifacts is a list of Helm charts that will be built as ExternalArtifacts + These artifacts can be referenced by ApplicationDefinitions + items: + description: BundleArtifact defines a Helm chart artifact that will + be built as ExternalArtifact + properties: + libraries: + description: Libraries is a list of library names that this + artifact depends on + items: + type: string + type: array + name: + description: Name is the unique identifier for this artifact + (used as ExternalArtifact name) + type: string + path: + description: Path is the path to the Helm chart directory + type: string + required: + - name + - path + type: object + type: array + basePath: + description: |- + BasePath is the base path where packages are located in the source. + For GitRepository, defaults to "packages" if not specified. + For OCIRepository, defaults to empty string (root) if not specified. + type: string + deletionPolicy: + allOf: + - enum: + - Delete + - Orphan + - enum: + - Delete + - Orphan + default: Delete + description: |- + DeletionPolicy defines how child resources should be handled when the bundle is deleted. + - "Delete" (default): Child resources will be deleted when the bundle is deleted (via ownerReference). + - "Orphan": Child resources will be orphaned (ownerReferences will be removed). + type: string + dependencyTargets: + description: |- + DependencyTargets defines named groups of packages that can be referenced + by other bundles via dependsOn. Each target has a name and a list of packages. + items: + description: |- + BundleDependencyTarget defines a named group of packages that can be referenced + by other bundles via dependsOn + properties: + name: + description: Name is the unique identifier for this dependency + target + type: string + packages: + description: |- + Packages is a list of package names that belong to this target + These packages will be added as dependencies when this target is referenced + items: + type: string + type: array + required: + - name + - packages + type: object + type: array + dependsOn: + description: |- + DependsOn is a list of bundle dependencies in the format "bundleName/target" + For example: "cozystack-system/network" + If specified, the dependencies listed in the target's packages will be taken + from the specified bundle and added to all packages in this bundle + items: + type: string + type: array + labels: + additionalProperties: + type: string + description: |- + Labels are labels that will be applied to all resources created by this bundle + (ArtifactGenerators and HelmReleases). These labels are merged with the default + cozystack.io/bundle label. + type: object + libraries: + description: Libraries is a list of Helm library charts used by packages + items: + description: BundleLibrary defines a Helm library chart + properties: + name: + description: Name is the unique identifier for this library + type: string + path: + description: Path is the path to the library chart directory + type: string + required: + - name + - path + type: object + type: array + packages: + description: Packages is a list of Helm releases to be installed as + part of this bundle + items: + description: BundleRelease defines a single Helm release within + a bundle + properties: + artifact: + description: |- + Artifact is the name of an artifact from the bundle's artifacts list + The artifact must exist in the bundle's artifacts section + Either Path or Artifact must be specified, but not both + type: string + dependsOn: + description: DependsOn is a list of release names that must + be installed before this release + items: + type: string + type: array + disabled: + description: Disabled indicates whether this release is disabled + (should not be installed) + type: boolean + labels: + additionalProperties: + type: string + description: |- + Labels are labels that will be applied to the HelmRelease created for this package + These labels are merged with bundle-level labels and the default cozystack.io/bundle label + type: object + libraries: + description: Libraries is a list of library names that this + package depends on + items: + type: string + type: array + name: + description: Name is the unique identifier for this release + within the bundle + type: string + namespace: + description: Namespace is the Kubernetes namespace where the + release will be installed + type: string + namespaceAnnotations: + additionalProperties: + type: string + description: |- + NamespaceAnnotations are annotations that will be applied to the namespace for this package + These annotations are merged with annotations from other packages in the same namespace + type: object + namespaceLabels: + additionalProperties: + type: string + description: |- + NamespaceLabels are labels that will be applied to the namespace for this package + These labels are merged with labels from other packages in the same namespace + type: object + path: + description: |- + Path is the path to the Helm chart directory + Either Path or Artifact must be specified, but not both + type: string + privileged: + description: Privileged indicates whether this release requires + privileged access + type: boolean + releaseName: + description: ReleaseName is the name of the HelmRelease resource + that will be created + type: string + values: + description: Values contains Helm chart values as a JSON object + x-kubernetes-preserve-unknown-fields: true + valuesFiles: + description: ValuesFiles is a list of values file names to use + items: + type: string + type: array + required: + - name + - namespace + - releaseName + type: object + x-kubernetes-validations: + - message: either path or artifact must be set, but not both + rule: (has(self.path) && !has(self.artifact)) || (!has(self.path) + && has(self.artifact)) + type: array + sourceRef: + description: SourceRef is the source reference for the bundle charts + properties: + kind: + description: Kind of the source reference + enum: + - GitRepository + - OCIRepository + type: string + name: + description: Name of the source reference + type: string + namespace: + description: Namespace of the source reference + type: string + required: + - kind + - name + - namespace + type: object + required: + - packages + - sourceRef + type: object + type: object + served: true + storage: true diff --git a/packages/core/installer/crds/cozystack.io_platforms.yaml b/packages/core/installer/crds/cozystack.io_platforms.yaml new file mode 100644 index 00000000..74364b34 --- /dev/null +++ b/packages/core/installer/crds/cozystack.io_platforms.yaml @@ -0,0 +1,85 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: platforms.cozystack.io +spec: + group: cozystack.io + names: + kind: Platform + listKind: PlatformList + plural: platforms + shortNames: + - platform + singular: platform + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Platform is the Schema for the platforms API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PlatformSpec defines the desired state of Platform + properties: + basePath: + description: |- + BasePath is the base path where the platform chart is located in the source. + For GitRepository, defaults to "packages/core/platform" if not specified. + For OCIRepository, defaults to "core/platform" if not specified. + type: string + interval: + default: 5m + description: Interval is the interval at which to reconcile the HelmRelease + type: string + sourceRef: + description: |- + SourceRef is the source reference for the platform chart + This is used to generate the ArtifactGenerator + properties: + kind: + default: HelmRepository + description: Kind of the source reference + type: string + name: + description: Name of the source reference + type: string + namespace: + default: cozy-public + description: Namespace of the source reference + type: string + required: + - kind + - name + - namespace + type: object + values: + description: |- + Values contains Helm chart values as a JSON object + These values are passed directly to HelmRelease.values + x-kubernetes-preserve-unknown-fields: true + required: + - sourceRef + type: object + type: object + served: true + storage: true diff --git a/packages/core/installer/example/platform.yaml b/packages/core/installer/example/platform.yaml new file mode 100644 index 00000000..1982cbe6 --- /dev/null +++ b/packages/core/installer/example/platform.yaml @@ -0,0 +1,30 @@ +apiVersion: cozystack.io/v1alpha1 +kind: Platform +metadata: + name: cozystack-platform +spec: + interval: 5m + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + values: + bundles: + system: + type: "full" + iaas: + enabled: true + paas: + enabled: true + naas: + enabled: false + publishing: + host: "dev5.infra.aenix.org" + apiServerEndpoint: "https://api.dev5.infra.aenix.org" + externalIPs: + - 10.4.0.246 + - 10.4.0.180 + - 10.4.0.46 + authentication: + oidc: + enabled: true diff --git a/packages/core/installer/images/cozystack-operator/Dockerfile b/packages/core/installer/images/cozystack-operator/Dockerfile new file mode 100644 index 00000000..c879df34 --- /dev/null +++ b/packages/core/installer/images/cozystack-operator/Dockerfile @@ -0,0 +1,24 @@ +FROM golang:1.25-alpine as builder + +ARG TARGETOS +ARG TARGETARCH + +RUN apk add --no-cache make git +RUN apk add helm --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community + +COPY . /src/ +WORKDIR /src + +RUN go mod download + +# Build cozystack-operator +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build \ + -ldflags="-w -s" \ + -o /cozystack-operator \ + ./cmd/cozystack-operator + +FROM alpine:3.22 + +COPY --from=builder /cozystack-operator /usr/bin/cozystack-operator + +ENTRYPOINT ["/usr/bin/cozystack-operator"] diff --git a/packages/core/installer/images/cozystack-operator/Dockerfile.dockerignore b/packages/core/installer/images/cozystack-operator/Dockerfile.dockerignore new file mode 100644 index 00000000..6803982e --- /dev/null +++ b/packages/core/installer/images/cozystack-operator/Dockerfile.dockerignore @@ -0,0 +1,12 @@ +# Exclude everything except src directory +* +!src/** +!api/** +!cmd/** +!hack/** +!internal/** +!packages/** +!pkg/** +!scripts/** +!go.mod +!go.sum diff --git a/packages/core/installer/images/cozystack/Dockerfile b/packages/core/installer/images/cozystack/Dockerfile deleted file mode 100644 index 7327698e..00000000 --- a/packages/core/installer/images/cozystack/Dockerfile +++ /dev/null @@ -1,48 +0,0 @@ -FROM golang:1.24-alpine as k8s-await-election-builder - -ARG K8S_AWAIT_ELECTION_GITREPO=https://github.com/LINBIT/k8s-await-election -ARG K8S_AWAIT_ELECTION_VERSION=0.4.1 - -# TARGETARCH is a docker special variable: https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope -ARG TARGETARCH - -RUN apk add --no-cache git make -RUN git clone ${K8S_AWAIT_ELECTION_GITREPO} /usr/local/go/k8s-await-election/ \ - && cd /usr/local/go/k8s-await-election \ - && git reset --hard v${K8S_AWAIT_ELECTION_VERSION} \ - && make \ - && mv ./out/k8s-await-election-${TARGETARCH} /k8s-await-election - -FROM golang:1.24-alpine as builder - -ARG TARGETOS -ARG TARGETARCH - -RUN apk add --no-cache make git -RUN apk add helm --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community - -COPY . /src/ -WORKDIR /src - -RUN go mod download - -RUN go build -o /cozystack-assets-server -ldflags '-extldflags "-static" -w -s' ./cmd/cozystack-assets-server - -RUN make repos - -FROM alpine:3.22 - -RUN wget -O- https://github.com/cozystack/cozypkg/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.2.0 - -RUN apk add --no-cache make kubectl helm coreutils git jq - -COPY --from=builder /src/scripts /cozystack/scripts -COPY --from=builder /src/packages/core /cozystack/packages/core -COPY --from=builder /src/packages/system /cozystack/packages/system -COPY --from=builder /src/_out/repos /cozystack/assets/repos -COPY --from=builder /cozystack-assets-server /usr/bin/cozystack-assets-server -COPY --from=k8s-await-election-builder /k8s-await-election /usr/bin/k8s-await-election -COPY --from=builder /src/dashboards /cozystack/assets/dashboards - -WORKDIR /cozystack -ENTRYPOINT ["/usr/bin/k8s-await-election", "/cozystack/scripts/installer.sh" ] diff --git a/packages/core/installer/images/cozystack/Dockerfile.dockerignore b/packages/core/installer/images/cozystack/Dockerfile.dockerignore deleted file mode 100644 index c1d18d8a..00000000 --- a/packages/core/installer/images/cozystack/Dockerfile.dockerignore +++ /dev/null @@ -1 +0,0 @@ -_out diff --git a/packages/core/installer/templates/cozystack.yaml b/packages/core/installer/templates/cozystack-operator.yaml similarity index 56% rename from packages/core/installer/templates/cozystack.yaml rename to packages/core/installer/templates/cozystack-operator.yaml index 10ebdc1f..e3fa48b4 100644 --- a/packages/core/installer/templates/cozystack.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -1,3 +1,15 @@ +{{- define "cozystack-operator.flux-resource" -}} +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: cozystack-packages + namespace: cozy-system +spec: + interval: 5m0s + url: {{ .Values.cozystackOperator.packagesRepository }} + ref: + digest: {{ .Values.cozystackOperator.packagesDigest }} +{{- end -}} --- apiVersion: v1 kind: Namespace @@ -29,13 +41,13 @@ roleRef: apiVersion: apps/v1 kind: Deployment metadata: - name: cozystack + name: cozystack-operator namespace: cozy-system spec: replicas: 1 selector: matchLabels: - app: cozystack + app: cozystack-operator strategy: type: RollingUpdate rollingUpdate: @@ -44,39 +56,30 @@ spec: template: metadata: labels: - app: cozystack + app: cozystack-operator spec: - hostNetwork: true serviceAccountName: cozystack containers: - - name: cozystack - image: "{{ .Values.cozystack.image }}" + - name: cozystack-operator + image: "{{ .Values.cozystackOperator.image }}" + args: + - --leader-elect=true + - --install-flux=true + - --metrics-bind-address=0 + - --health-probe-bind-address= + - --cozystack-version={{ .Values.cozystackOperator.cozystackVersion }} + {{- if .Values.cozystackOperator.disableTelemetry }} + - --disable-telemetry + {{- end }} + {{- if .Values.cozystackOperator.packagesDigest }} + - --install-flux-resource={{ include "cozystack-operator.flux-resource" . | fromYaml | toJson }} + {{- end }} env: - name: KUBERNETES_SERVICE_HOST value: localhost - name: KUBERNETES_SERVICE_PORT value: "7445" - - name: K8S_AWAIT_ELECTION_ENABLED - value: "1" - - name: K8S_AWAIT_ELECTION_NAME - value: cozystack - - name: K8S_AWAIT_ELECTION_LOCK_NAME - value: cozystack - - name: K8S_AWAIT_ELECTION_LOCK_NAMESPACE - value: cozy-system - - name: K8S_AWAIT_ELECTION_IDENTITY - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: assets - image: "{{ .Values.cozystack.image }}" - command: - - /usr/bin/cozystack-assets-server - - "-dir=/cozystack/assets" - - "-address=:8123" - ports: - - name: http - containerPort: 8123 + hostNetwork: true tolerations: - key: "node.kubernetes.io/not-ready" operator: "Exists" @@ -84,17 +87,3 @@ spec: - key: "node.cilium.io/agent-not-ready" operator: "Exists" effect: "NoSchedule" ---- -apiVersion: v1 -kind: Service -metadata: - name: cozystack - namespace: cozy-system -spec: - ports: - - name: http - port: 80 - targetPort: 8123 - selector: - app: cozystack - type: ClusterIP diff --git a/packages/core/installer/templates/crds.yaml b/packages/core/installer/templates/crds.yaml new file mode 100644 index 00000000..bd51938d --- /dev/null +++ b/packages/core/installer/templates/crds.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "crds/*.yaml" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index dbb7b846..78f6cef0 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,2 +1,7 @@ cozystack: image: ghcr.io/cozystack/cozystack/installer:v0.38.2@sha256:9ff92b655de6f9bea3cba4cd42dcffabd9aace6966dcfb1cc02dda2420ea4a15 +cozystackOperator: + image: ghcr.io/cozystack/cozystack/cozystack-operator:latest@sha256:446b4afe9ed160a850cc9b61dd6f34b2ae36d69d4cd974d6d35a25180bd73879 + packagesRepository: oci://ghcr.io/cozystack/cozystack/platform-packages + packagesDigest: sha256:39666735999e79f3b7b502ff46c642e13a17ce8860a47493af0912285d360ec9 + cozystackVersion: latest diff --git a/packages/core/platform/Makefile b/packages/core/platform/Makefile index ab0e17bf..4ffc95f2 100644 --- a/packages/core/platform/Makefile +++ b/packages/core/platform/Makefile @@ -1,20 +1,30 @@ -NAME=platform +NAME=cozystack-platform NAMESPACE=cozy-system -show: - cozypkg show -n $(NAMESPACE) $(NAME) --plain +include ../../../hack/common-envs.mk +include ../../../hack/package.mk -apply: - cozypkg show -n $(NAMESPACE) $(NAME) --plain | kubectl apply -f- - kubectl delete helmreleases.helm.toolkit.fluxcd.io -l cozystack.io/marked-for-deletion=true -A +image: image-migrations -reconcile: apply +image-migrations: + docker buildx build images/migrations \ + --tag $(REGISTRY)/platform-migrations:$(call settag,$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/platform-migrations:latest \ + --cache-to type=inline \ + --metadata-file images/migrations.json \ + $(BUILDX_ARGS) + IMAGE="$(REGISTRY)/platform-migrations:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/migrations.json -o json -r)" \ + yq -i '.migrations.image = strenv(IMAGE)' values.yaml + rm -f images/migrations.json -namespaces-show: - cozypkg show -n $(NAMESPACE) $(NAME) --plain -s templates/namespaces.yaml - -namespaces-apply: - cozypkg show -n $(NAMESPACE) $(NAME) --plain -s templates/namespaces.yaml | kubectl apply -f- - -diff: - cozypkg show -n $(NAMESPACE) $(NAME) --plain | kubectl diff -f- +generate: + @echo "Generating target version from migrations..." + @LAST_MIGRATION=$$(ls -1 images/migrations/migrations/ 2>/dev/null | grep -E '^[0-9]+$$' | sort -n | tail -1); \ + if [ -z "$$LAST_MIGRATION" ]; then \ + echo "Error: No migration files found" >&2; \ + exit 1; \ + fi; \ + TARGET_VERSION=$$((LAST_MIGRATION + 1)); \ + echo "Last migration: $$LAST_MIGRATION, Target version: $$TARGET_VERSION"; \ + yq -i '.migrations.targetVersion = $$TARGET_VERSION' values.yaml; \ + echo "Updated targetVersion to $$TARGET_VERSION in values.yaml" diff --git a/packages/core/platform/bundles/distro-full.yaml b/packages/core/platform/bundles/distro-full.yaml deleted file mode 100644 index ed52a83a..00000000 --- a/packages/core/platform/bundles/distro-full.yaml +++ /dev/null @@ -1,280 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} - -releases: -- name: fluxcd-operator - releaseName: fluxcd-operator - chart: cozy-fluxcd-operator - namespace: cozy-fluxcd - privileged: true - dependsOn: [] - -- name: fluxcd - releaseName: fluxcd - chart: cozy-fluxcd - namespace: cozy-fluxcd - dependsOn: [fluxcd-operator,cilium] - values: - flux-instance: - instance: - cluster: - domain: {{ $clusterDomain }} - -- name: cilium - releaseName: cilium - chart: cozy-cilium - namespace: cozy-cilium - privileged: true - dependsOn: [] - valuesFiles: - - values.yaml - - values-talos.yaml - values: - cilium: - enableIPv4Masquerade: true - enableIdentityMark: true - ipv4NativeRoutingCIDR: "{{ index $cozyConfig.data "ipv4-pod-cidr" }}" - autoDirectNodeRoutes: true - routingMode: native - -- name: cilium-networkpolicy - releaseName: cilium-networkpolicy - chart: cozy-cilium-networkpolicy - namespace: cozy-cilium - privileged: true - dependsOn: [cilium] - -- name: cozy-proxy - releaseName: cozystack - chart: cozy-cozy-proxy - namespace: cozy-system - optional: true - dependsOn: [cilium] - -- name: cert-manager-crds - releaseName: cert-manager-crds - chart: cozy-cert-manager-crds - namespace: cozy-cert-manager - dependsOn: [cilium] - -- name: cozystack-controller - releaseName: cozystack-controller - chart: cozy-cozystack-controller - namespace: cozy-system - dependsOn: [cilium] - {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} - values: - cozystackController: - disableTelemetry: true - {{- end }} - -- name: lineage-controller-webhook - releaseName: lineage-controller-webhook - chart: cozy-lineage-controller-webhook - namespace: cozy-system - dependsOn: [cozystack-controller,cilium,cert-manager] - -- name: cert-manager - releaseName: cert-manager - chart: cozy-cert-manager - namespace: cozy-cert-manager - dependsOn: [cert-manager-crds] - -- name: cert-manager-issuers - releaseName: cert-manager-issuers - chart: cozy-cert-manager-issuers - namespace: cozy-cert-manager - optional: true - dependsOn: [cilium,cert-manager] - -- name: victoria-metrics-operator - releaseName: victoria-metrics-operator - chart: cozy-victoria-metrics-operator - namespace: cozy-victoria-metrics-operator - optional: true - dependsOn: [cilium,cert-manager] - -- name: monitoring-agents - releaseName: monitoring-agents - chart: cozy-monitoring-agents - namespace: cozy-monitoring - privileged: true - optional: true - dependsOn: [cilium,victoria-metrics-operator] - values: - scrapeRules: - etcd: - enabled: true - -- name: metallb - releaseName: metallb - chart: cozy-metallb - namespace: cozy-metallb - privileged: true - dependsOn: [cilium] - -- name: etcd-operator - releaseName: etcd-operator - chart: cozy-etcd-operator - namespace: cozy-etcd-operator - optional: true - dependsOn: [cilium,cert-manager] - -- name: grafana-operator - releaseName: grafana-operator - chart: cozy-grafana-operator - namespace: cozy-grafana-operator - optional: true - dependsOn: [cilium] - -- name: mariadb-operator - releaseName: mariadb-operator - chart: cozy-mariadb-operator - namespace: cozy-mariadb-operator - optional: true - dependsOn: [cilium,cert-manager,victoria-metrics-operator] - values: - mariadb-operator: - clusterName: {{ $clusterDomain }} - -- name: postgres-operator - releaseName: postgres-operator - chart: cozy-postgres-operator - namespace: cozy-postgres-operator - optional: true - dependsOn: [cilium,cert-manager,victoria-metrics-operator] - -- name: kafka-operator - releaseName: kafka-operator - chart: cozy-kafka-operator - namespace: cozy-kafka-operator - optional: true - dependsOn: [cilium,victoria-metrics-operator] - values: - strimzi-kafka-operator: - kubernetesServiceDnsDomain: {{ $clusterDomain }} - -- name: clickhouse-operator - releaseName: clickhouse-operator - chart: cozy-clickhouse-operator - namespace: cozy-clickhouse-operator - optional: true - dependsOn: [cilium,victoria-metrics-operator] - -- name: foundationdb-operator - releaseName: foundationdb-operator - chart: cozy-foundationdb-operator - namespace: cozy-foundationdb-operator - optional: true - dependsOn: [cilium,cert-manager] - -- name: rabbitmq-operator - releaseName: rabbitmq-operator - chart: cozy-rabbitmq-operator - namespace: cozy-rabbitmq-operator - optional: true - dependsOn: [cilium] - -- name: redis-operator - releaseName: redis-operator - chart: cozy-redis-operator - namespace: cozy-redis-operator - optional: true - dependsOn: [cilium] - -- name: piraeus-operator - releaseName: piraeus-operator - chart: cozy-piraeus-operator - namespace: cozy-linstor - dependsOn: [cilium,cert-manager] - -- name: snapshot-controller - releaseName: snapshot-controller - chart: cozy-snapshot-controller - namespace: cozy-snapshot-controller - dependsOn: [cilium] - -- name: objectstorage-controller - releaseName: objectstorage-controller - chart: cozy-objectstorage-controller - namespace: cozy-objectstorage-controller - optional: true - dependsOn: [cilium] - -- name: linstor - releaseName: linstor - chart: cozy-linstor - namespace: cozy-linstor - privileged: true - dependsOn: [piraeus-operator,cilium,cert-manager,snapshot-controller] - -- name: nfs-driver - releaseName: nfs-driver - chart: cozy-nfs-driver - namespace: cozy-nfs-driver - privileged: true - dependsOn: [cilium] - optional: true - -- name: telepresence - releaseName: traffic-manager - chart: cozy-telepresence - namespace: cozy-telepresence - optional: true - dependsOn: [] - -- name: external-dns - releaseName: external-dns - chart: cozy-external-dns - namespace: cozy-external-dns - optional: true - dependsOn: [cilium] - -- name: external-secrets-operator - releaseName: external-secrets-operator - chart: cozy-external-secrets-operator - namespace: cozy-external-secrets-operator - optional: true - dependsOn: [cilium] - -- name: keycloak - releaseName: keycloak - chart: cozy-keycloak - namespace: cozy-keycloak - optional: true - dependsOn: [postgres-operator] - -- name: keycloak-operator - releaseName: keycloak-operator - chart: cozy-keycloak-operator - namespace: cozy-keycloak - optional: true - dependsOn: [keycloak] - -- name: bootbox - releaseName: bootbox - chart: cozy-bootbox - namespace: cozy-bootbox - privileged: true - optional: true - dependsOn: [cilium] - -- name: reloader - releaseName: reloader - chart: cozy-reloader - namespace: cozy-reloader - -- name: velero - releaseName: velero - chart: cozy-velero - namespace: cozy-velero - privileged: true - optional: true - dependsOn: [cilium] - -- name: hetzner-robotlb - releaseName: robotlb - optional: true - chart: cozy-hetzner-robotlb - namespace: cozy-hetzner-robotlb - dependsOn: [cilium] diff --git a/packages/core/platform/bundles/distro-hosted.yaml b/packages/core/platform/bundles/distro-hosted.yaml deleted file mode 100644 index 83aa81d9..00000000 --- a/packages/core/platform/bundles/distro-hosted.yaml +++ /dev/null @@ -1,192 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} - -releases: -- name: fluxcd-operator - releaseName: fluxcd-operator - chart: cozy-fluxcd-operator - namespace: cozy-fluxcd - privileged: true - dependsOn: [] - -- name: fluxcd - releaseName: fluxcd - chart: cozy-fluxcd - namespace: cozy-fluxcd - dependsOn: [fluxcd-operator] - values: - flux-instance: - instance: - cluster: - domain: {{ $clusterDomain }} - -- name: cert-manager-crds - releaseName: cert-manager-crds - chart: cozy-cert-manager-crds - namespace: cozy-cert-manager - dependsOn: [] - -- name: cozystack-controller - releaseName: cozystack-controller - chart: cozy-cozystack-controller - namespace: cozy-system - {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} - values: - cozystackController: - disableTelemetry: true - {{- end }} - -- name: lineage-controller-webhook - releaseName: lineage-controller-webhook - chart: cozy-lineage-controller-webhook - namespace: cozy-system - dependsOn: [cozystack-controller,cert-manager] - -- name: cert-manager - releaseName: cert-manager - chart: cozy-cert-manager - namespace: cozy-cert-manager - dependsOn: [cert-manager-crds] - -- name: cert-manager-issuers - releaseName: cert-manager-issuers - chart: cozy-cert-manager-issuers - namespace: cozy-cert-manager - optional: true - dependsOn: [cert-manager] - -- name: victoria-metrics-operator - releaseName: victoria-metrics-operator - chart: cozy-victoria-metrics-operator - namespace: cozy-victoria-metrics-operator - optional: true - dependsOn: [cert-manager] - -- name: monitoring-agents - releaseName: monitoring-agents - chart: cozy-monitoring-agents - namespace: cozy-monitoring - privileged: true - optional: true - dependsOn: [victoria-metrics-operator] - values: - scrapeRules: - etcd: - enabled: true - -- name: etcd-operator - releaseName: etcd-operator - chart: cozy-etcd-operator - namespace: cozy-etcd-operator - optional: true - dependsOn: [cert-manager] - -- name: grafana-operator - releaseName: grafana-operator - chart: cozy-grafana-operator - namespace: cozy-grafana-operator - optional: true - dependsOn: [] - -- name: mariadb-operator - releaseName: mariadb-operator - chart: cozy-mariadb-operator - namespace: cozy-mariadb-operator - optional: true - dependsOn: [victoria-metrics-operator] - values: - mariadb-operator: - clusterName: {{ $clusterDomain }} - -- name: postgres-operator - releaseName: postgres-operator - chart: cozy-postgres-operator - namespace: cozy-postgres-operator - optional: true - dependsOn: [victoria-metrics-operator] - -- name: kafka-operator - releaseName: kafka-operator - chart: cozy-kafka-operator - namespace: cozy-kafka-operator - optional: true - dependsOn: [victoria-metrics-operator] - values: - strimzi-kafka-operator: - kubernetesServiceDnsDomain: {{ $clusterDomain }} - -- name: clickhouse-operator - releaseName: clickhouse-operator - chart: cozy-clickhouse-operator - namespace: cozy-clickhouse-operator - optional: true - dependsOn: [victoria-metrics-operator] - -- name: foundationdb-operator - releaseName: foundationdb-operator - chart: cozy-foundationdb-operator - namespace: cozy-foundationdb-operator - optional: true - dependsOn: [cert-manager] - -- name: rabbitmq-operator - releaseName: rabbitmq-operator - chart: cozy-rabbitmq-operator - namespace: cozy-rabbitmq-operator - optional: true - dependsOn: [] - -- name: redis-operator - releaseName: redis-operator - chart: cozy-redis-operator - namespace: cozy-redis-operator - optional: true - dependsOn: [] - -- name: telepresence - releaseName: traffic-manager - chart: cozy-telepresence - namespace: cozy-telepresence - optional: true - dependsOn: [] - -- name: external-dns - releaseName: external-dns - chart: cozy-external-dns - namespace: cozy-external-dns - optional: true - dependsOn: [] - -- name: external-secrets-operator - releaseName: external-secrets-operator - chart: cozy-external-secrets-operator - namespace: cozy-external-secrets-operator - optional: true - dependsOn: [] - -- name: keycloak - releaseName: keycloak - chart: cozy-keycloak - namespace: cozy-keycloak - optional: true - dependsOn: [postgres-operator] - -- name: keycloak-operator - releaseName: keycloak-operator - chart: cozy-keycloak-operator - namespace: cozy-keycloak - optional: true - dependsOn: [keycloak] - -- name: velero - releaseName: velero - chart: cozy-velero - namespace: cozy-velero - privileged: true - optional: true - -- name: hetzner-robotlb - releaseName: robotlb - optional: true - chart: cozy-hetzner-robotlb - namespace: cozy-hetzner-robotlb diff --git a/packages/system/cozystack-resource-definitions/cozyrds/bucket.yaml b/packages/core/platform/bundles/iaas/applicationdefinitions/bucket.yaml similarity index 89% rename from packages/system/cozystack-resource-definitions/cozyrds/bucket.yaml rename to packages/core/platform/bundles/iaas/applicationdefinitions/bucket.yaml index 889a36b5..9ed4a9c7 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/bucket.yaml +++ b/packages/core/platform/bundles/iaas/applicationdefinitions/bucket.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: bucket spec: @@ -13,12 +13,14 @@ spec: prefix: bucket- labels: cozystack.io/ui: "true" - chart: - name: bucket + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-iaas-bucket + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: singular: Bucket plural: Buckets @@ -33,10 +35,10 @@ spec: exclude: [] include: - resourceNames: - - bucket-{{ .name }} - - bucket-{{ .name }}-credentials + - "{{`bucket-{{ .name }}`}}" + - "{{`bucket-{{ .name }}-credentials`}}" ingresses: exclude: [] include: - resourceNames: - - bucket-{{ .name }}-ui + - "{{`bucket-{{ .name }}-ui`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/kubernetes.yaml b/packages/core/platform/bundles/iaas/applicationdefinitions/kubernetes.yaml similarity index 98% rename from packages/system/cozystack-resource-definitions/cozyrds/kubernetes.yaml rename to packages/core/platform/bundles/iaas/applicationdefinitions/kubernetes.yaml index 86e8694d..59a24e78 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/kubernetes.yaml +++ b/packages/core/platform/bundles/iaas/applicationdefinitions/kubernetes.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: kubernetes spec: @@ -13,12 +13,14 @@ spec: prefix: kubernetes- labels: cozystack.io/ui: "true" - chart: - name: kubernetes + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-iaas-kubernetes + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: IaaS singular: Kubernetes @@ -33,14 +35,14 @@ spec: exclude: [] include: - resourceNames: - - kubernetes-{{ .name }}-admin-kubeconfig + - "{{`kubernetes-{{ .name }}-admin-kubeconfig`}}" services: exclude: [] include: - resourceNames: - - kubernetes-{{ .name }} + - "{{`kubernetes-{{ .name }}`}}" ingresses: exclude: [] include: - resourceNames: - - kubernetes-{{ .name }} + - "{{`kubernetes-{{ .name }}`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/virtual-machine.yaml b/packages/core/platform/bundles/iaas/applicationdefinitions/virtual-machine.yaml similarity index 96% rename from packages/system/cozystack-resource-definitions/cozyrds/virtual-machine.yaml rename to packages/core/platform/bundles/iaas/applicationdefinitions/virtual-machine.yaml index a1e384c4..0b3c48f8 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/virtual-machine.yaml +++ b/packages/core/platform/bundles/iaas/applicationdefinitions/virtual-machine.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: virtual-machine spec: @@ -13,12 +13,14 @@ spec: prefix: virtual-machine- labels: cozystack.io/ui: "true" - chart: - name: virtual-machine + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-iaas-virtual-machine + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: IaaS singular: Virtual Machine @@ -36,4 +38,4 @@ spec: exclude: [] include: - resourceNames: - - virtual-machine-{{ .name }} + - "{{`virtual-machine-{{ .name }}`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/virtualprivatecloud.yaml b/packages/core/platform/bundles/iaas/applicationdefinitions/virtualprivatecloud.yaml similarity index 91% rename from packages/system/cozystack-resource-definitions/cozyrds/virtualprivatecloud.yaml rename to packages/core/platform/bundles/iaas/applicationdefinitions/virtualprivatecloud.yaml index 8d05a5b6..783f025b 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/virtualprivatecloud.yaml +++ b/packages/core/platform/bundles/iaas/applicationdefinitions/virtualprivatecloud.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: virtualprivatecloud spec: @@ -13,12 +13,14 @@ spec: prefix: "virtualprivatecloud-" labels: cozystack.io/ui: "true" - chart: - name: virtualprivatecloud + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-iaas-virtualprivatecloud + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: IaaS singular: VPC diff --git a/packages/system/cozystack-resource-definitions/cozyrds/vm-disk.yaml b/packages/core/platform/bundles/iaas/applicationdefinitions/vm-disk.yaml similarity index 95% rename from packages/system/cozystack-resource-definitions/cozyrds/vm-disk.yaml rename to packages/core/platform/bundles/iaas/applicationdefinitions/vm-disk.yaml index c3c1b830..1cf255f0 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/vm-disk.yaml +++ b/packages/core/platform/bundles/iaas/applicationdefinitions/vm-disk.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: vm-disk spec: @@ -13,12 +13,14 @@ spec: prefix: vm-disk- labels: cozystack.io/ui: "true" - chart: - name: vm-disk + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-iaas-vm-disk + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: IaaS singular: VM Disk diff --git a/packages/system/cozystack-resource-definitions/cozyrds/vm-instance.yaml b/packages/core/platform/bundles/iaas/applicationdefinitions/vm-instance.yaml similarity index 97% rename from packages/system/cozystack-resource-definitions/cozyrds/vm-instance.yaml rename to packages/core/platform/bundles/iaas/applicationdefinitions/vm-instance.yaml index 58eb5f9a..34b06c1b 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/vm-instance.yaml +++ b/packages/core/platform/bundles/iaas/applicationdefinitions/vm-instance.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: vm-instance spec: @@ -13,12 +13,14 @@ spec: prefix: vm-instance- labels: cozystack.io/ui: "true" - chart: - name: vm-instance + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-iaas-vm-instance + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: IaaS singular: VM Instance diff --git a/packages/core/platform/bundles/iaas/bundle.yaml b/packages/core/platform/bundles/iaas/bundle.yaml new file mode 100644 index 00000000..c45bd74f --- /dev/null +++ b/packages/core/platform/bundles/iaas/bundle.yaml @@ -0,0 +1,182 @@ +{{- $clusterDomain := .Values.networking.clusterDomain | default "cozy.local" }} +{{- $host := .Values.publishing.host }} +{{- if not $host }} +{{- fail "ERROR: publishing.host is required" }} +{{- end }} + +apiVersion: cozystack.io/v1alpha1 +kind: Bundle +metadata: + name: cozystack-iaas +spec: + dependsOn: [cozystack-system/network] + + sourceRef: + kind: {{ .Values.sourceRef.kind }} + name: {{ .Values.sourceRef.name }} + namespace: {{ .Values.sourceRef.namespace }} + + libraries: + - name: cozy-lib + path: library/cozy-lib + + artifacts: + - name: bucket + path: apps/bucket + libraries: [cozy-lib] + - name: bucket-system + path: apps/bucket + libraries: [cozy-lib] + + - name: kubernetes + path: apps/kubernetes + libraries: [cozy-lib] + - name: kubernetes-ingress-nginx + path: system/ingress-nginx + - name: kubernetes-cert-manager-crds + path: system/cert-manager-crds + - name: kubernetes-volumesnapshot-crd + path: system/vsnap-crd + - name: kubernetes-velero + path: system/velero + - name: kubernetes-gateway-api-crds + path: system/gateway-api-crds + - name: kubernetes-victoria-metrics-operator + path: system/victoria-metrics-operator + - name: kubernetes-kubevirt-csi-node + path: system/kubevirt-csi-node + - name: kubernetes-vertical-pod-autoscaler-crds + path: system/vertical-pod-autoscaler-crds + - name: kubernetes-coredns + path: system/coredns + - name: kubernetes-cert-manager + path: system/cert-manager + - name: kubernetes-fluxcd-operator + path: system/fluxcd-operator + - name: kubernetes-fluxcd + path: system/fluxcd + - name: kubernetes-monitoring-agents + path: system/monitoring-agents + - name: kubernetes-cilium + path: system/cilium + - name: kubernetes-gpu-operator + path: system/gpu-operator + - name: kubernetes-vertical-pod-autoscaler + path: system/vertical-pod-autoscaler + + - name: virtual-machine + path: apps/virtual-machine + libraries: [cozy-lib] + + - name: vpc + path: apps/vpc + libraries: [cozy-lib] + + - name: virtualprivatecloud + path: apps/vpc + libraries: [cozy-lib] + + - name: vm-disk + path: apps/vm-disk + libraries: [cozy-lib] + + - name: vm-instance + path: apps/vm-instance + libraries: [cozy-lib] + + packages: + - name: kubevirt-operator + releaseName: kubevirt-operator + path: system/kubevirt-operator + namespace: cozy-kubevirt + dependsOn: [victoria-metrics-operator] + + - name: kubevirt + releaseName: kubevirt + path: system/kubevirt + namespace: cozy-kubevirt + privileged: true + dependsOn: [kubevirt-operator] + values: + cpuAllocationRatio: {{ .Values.resources.cpuAllocationRatio }} + cozystack: + publishing: + host: {{ $host }} + exposedServices: {{ .Values.publishing.exposedServices | default list | toJson }} + ingressClassName: {{ .Values.publishing.ingressName | default "tenant-root" }} + + - name: kubevirt-instancetypes + releaseName: kubevirt-instancetypes + path: system/kubevirt-instancetypes + namespace: cozy-kubevirt + dependsOn: [kubevirt-operator,kubevirt] + + - name: kubevirt-cdi-operator + releaseName: kubevirt-cdi-operator + path: system/kubevirt-cdi-operator + namespace: cozy-kubevirt-cdi + dependsOn: [] + + - name: kubevirt-cdi + releaseName: kubevirt-cdi + path: system/kubevirt-cdi + namespace: cozy-kubevirt-cdi + dependsOn: [kubevirt-cdi-operator] + values: + cozystack: + publishing: + host: {{ $host }} + exposedServices: {{ .Values.publishing.exposedServices | default list | toJson }} + ingressClassName: {{ .Values.publishing.ingressName | default "tenant-root" }} + + - name: gpu-operator + releaseName: gpu-operator + path: system/gpu-operator + namespace: cozy-gpu-operator + privileged: true + disabled: true + dependsOn: [] + valuesFiles: + - values.yaml + - values-talos.yaml + + - name: kamaji + releaseName: kamaji + path: system/kamaji + namespace: cozy-kamaji + dependsOn: [] + + - name: capi-operator + releaseName: capi-operator + path: system/capi-operator + namespace: cozy-cluster-api + privileged: true + dependsOn: [] + + - name: capi-providers-bootstrap + releaseName: capi-providers-bootstrap + path: system/capi-providers-bootstrap + namespace: cozy-cluster-api + privileged: true + dependsOn: [capi-operator] + + - name: capi-providers-core + releaseName: capi-providers-core + path: system/capi-providers-core + namespace: cozy-cluster-api + privileged: true + dependsOn: [capi-operator] + + - name: capi-providers-cpprovider + releaseName: capi-providers-cpprovider + path: system/capi-providers-cpprovider + namespace: cozy-cluster-api + privileged: true + dependsOn: [capi-operator] + + - name: capi-providers-infraprovider + releaseName: capi-providers-infraprovider + path: system/capi-providers-infraprovider + namespace: cozy-cluster-api + privileged: true + dependsOn: [capi-operator] diff --git a/packages/system/cozystack-resource-definitions/cozyrds/http-cache.yaml b/packages/core/platform/bundles/naas/applicationdefinitions/http-cache.yaml similarity index 96% rename from packages/system/cozystack-resource-definitions/cozyrds/http-cache.yaml rename to packages/core/platform/bundles/naas/applicationdefinitions/http-cache.yaml index 70de0418..788d7a06 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/http-cache.yaml +++ b/packages/core/platform/bundles/naas/applicationdefinitions/http-cache.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: http-cache spec: @@ -13,12 +13,14 @@ spec: prefix: http-cache- labels: cozystack.io/ui: "true" - chart: - name: http-cache + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-naas-http-cache + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: NaaS singular: HTTP Cache diff --git a/packages/system/cozystack-resource-definitions/cozyrds/tcp-balancer.yaml b/packages/core/platform/bundles/naas/applicationdefinitions/tcp-balancer.yaml similarity index 98% rename from packages/system/cozystack-resource-definitions/cozyrds/tcp-balancer.yaml rename to packages/core/platform/bundles/naas/applicationdefinitions/tcp-balancer.yaml index 057bc922..e6e7ca81 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/tcp-balancer.yaml +++ b/packages/core/platform/bundles/naas/applicationdefinitions/tcp-balancer.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: tcp-balancer spec: @@ -13,12 +13,14 @@ spec: prefix: tcp-balancer- labels: cozystack.io/ui: "true" - chart: - name: tcp-balancer + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-naas-tcp-balancer + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: NaaS singular: TCP Balancer diff --git a/packages/system/cozystack-resource-definitions/cozyrds/vpn.yaml b/packages/core/platform/bundles/naas/applicationdefinitions/vpn.yaml similarity index 94% rename from packages/system/cozystack-resource-definitions/cozyrds/vpn.yaml rename to packages/core/platform/bundles/naas/applicationdefinitions/vpn.yaml index 59d940c0..b1bff7bc 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/vpn.yaml +++ b/packages/core/platform/bundles/naas/applicationdefinitions/vpn.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: vpn spec: @@ -13,12 +13,14 @@ spec: prefix: vpn- labels: cozystack.io/ui: "true" - chart: - name: vpn + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-naas-vpn + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: NaaS singular: VPN @@ -32,9 +34,9 @@ spec: exclude: [] include: - resourceNames: - - vpn-{{ .name }}-urls + - "{{`vpn-{{ .name }}-urls`}}" services: exclude: [] include: - resourceNames: - - vpn-{{ .name }}-vpn + - "{{`vpn-{{ .name }}-vpn`}}" diff --git a/packages/core/platform/bundles/naas/bundle.yaml b/packages/core/platform/bundles/naas/bundle.yaml new file mode 100644 index 00000000..0a5f86c6 --- /dev/null +++ b/packages/core/platform/bundles/naas/bundle.yaml @@ -0,0 +1,30 @@ +apiVersion: cozystack.io/v1alpha1 +kind: Bundle +metadata: + name: cozystack-naas +spec: + dependsOn: [cozystack-system/network] + + sourceRef: + kind: {{ .Values.sourceRef.kind }} + name: {{ .Values.sourceRef.name }} + namespace: {{ .Values.sourceRef.namespace }} + + libraries: + - name: cozy-lib + path: library/cozy-lib + + artifacts: + - name: http-cache + path: apps/http-cache + libraries: [cozy-lib] + + - name: tcp-balancer + path: apps/tcp-balancer + libraries: [cozy-lib] + + - name: vpn + path: apps/vpn + libraries: [cozy-lib] + + packages: [] diff --git a/packages/core/platform/bundles/paas-full.yaml b/packages/core/platform/bundles/paas-full.yaml deleted file mode 100644 index 4382bd62..00000000 --- a/packages/core/platform/bundles/paas-full.yaml +++ /dev/null @@ -1,461 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} -{{- $oidcEnabled := index $cozyConfig.data "oidc-enabled" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- if not $host }} -{{- fail "ERROR need root-host in cozystack ConfigMap" }} -{{- end }} -{{- $apiServerEndpoint := index $cozyConfig.data "api-server-endpoint" }} -{{- if not $apiServerEndpoint }} -{{- fail "ERROR need api-server-endpoint in cozystack ConfigMap" }} -{{- end }} - -releases: -- name: fluxcd-operator - releaseName: fluxcd-operator - chart: cozy-fluxcd-operator - namespace: cozy-fluxcd - privileged: true - dependsOn: [] - -- name: fluxcd - releaseName: fluxcd - chart: cozy-fluxcd - namespace: cozy-fluxcd - dependsOn: [fluxcd-operator,cilium,kubeovn] - values: - flux-instance: - instance: - cluster: - domain: {{ $clusterDomain }} - -- name: cilium - releaseName: cilium - chart: cozy-cilium - namespace: cozy-cilium - privileged: true - dependsOn: [] - valuesFiles: - - values.yaml - - values-talos.yaml - - values-kubeovn.yaml - -- name: cilium-networkpolicy - releaseName: cilium-networkpolicy - chart: cozy-cilium-networkpolicy - namespace: cozy-cilium - privileged: true - dependsOn: [cilium] - -- name: kubeovn - releaseName: kubeovn - chart: cozy-kubeovn - namespace: cozy-kubeovn - privileged: true - dependsOn: [cilium] - values: - cozystack: - nodesHash: {{ include "cozystack.master-node-ips" . | sha256sum }} - kube-ovn: - ipv4: - POD_CIDR: "{{ index $cozyConfig.data "ipv4-pod-cidr" }}" - POD_GATEWAY: "{{ index $cozyConfig.data "ipv4-pod-gateway" }}" - SVC_CIDR: "{{ index $cozyConfig.data "ipv4-svc-cidr" }}" - JOIN_CIDR: "{{ index $cozyConfig.data "ipv4-join-cidr" }}" - -- name: kubeovn-webhook - releaseName: kubeovn-webhook - chart: cozy-kubeovn-webhook - namespace: cozy-kubeovn - privileged: true - dependsOn: [cilium,kubeovn,cert-manager] - -- name: kubeovn-plunger - releaseName: kubeovn-plunger - chart: cozy-kubeovn-plunger - namespace: cozy-kubeovn - dependsOn: [cilium,kubeovn] - -- name: multus - releaseName: multus - chart: cozy-multus - namespace: cozy-multus - privileged: true - dependsOn: [cilium,kubeovn] - -- name: cozy-proxy - releaseName: cozystack - chart: cozy-cozy-proxy - namespace: cozy-system - dependsOn: [cilium,kubeovn] - -- name: cert-manager-crds - releaseName: cert-manager-crds - chart: cozy-cert-manager-crds - namespace: cozy-cert-manager - dependsOn: [cilium, kubeovn] - -- name: cozystack-api - releaseName: cozystack-api - chart: cozy-cozystack-api - namespace: cozy-system - dependsOn: [cilium,kubeovn,cozystack-controller] - -- name: cozystack-controller - releaseName: cozystack-controller - chart: cozy-cozystack-controller - namespace: cozy-system - dependsOn: [cilium,kubeovn] - {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} - values: - cozystackController: - disableTelemetry: true - {{- end }} - -- name: lineage-controller-webhook - releaseName: lineage-controller-webhook - chart: cozy-lineage-controller-webhook - namespace: cozy-system - dependsOn: [cozystack-controller,cilium,kubeovn,cert-manager] - -- name: cozystack-resource-definition-crd - releaseName: cozystack-resource-definition-crd - chart: cozystack-resource-definition-crd - namespace: cozy-system - dependsOn: [cilium,kubeovn,cozystack-api,cozystack-controller] - -- name: cozystack-resource-definitions - releaseName: cozystack-resource-definitions - chart: cozystack-resource-definitions - namespace: cozy-system - dependsOn: [cilium,kubeovn,cozystack-api,cozystack-controller,cozystack-resource-definition-crd] - -- name: cert-manager - releaseName: cert-manager - chart: cozy-cert-manager - namespace: cozy-cert-manager - dependsOn: [cert-manager-crds] - -- name: cert-manager-issuers - releaseName: cert-manager-issuers - chart: cozy-cert-manager-issuers - namespace: cozy-cert-manager - dependsOn: [cilium,kubeovn,cert-manager] - -- name: victoria-metrics-operator - releaseName: victoria-metrics-operator - chart: cozy-victoria-metrics-operator - namespace: cozy-victoria-metrics-operator - dependsOn: [cilium,kubeovn,cert-manager] - -- name: monitoring-agents - releaseName: monitoring-agents - chart: cozy-monitoring-agents - namespace: cozy-monitoring - privileged: true - dependsOn: [victoria-metrics-operator, vertical-pod-autoscaler-crds] - values: - scrapeRules: - etcd: - enabled: true - -- name: kubevirt-operator - releaseName: kubevirt-operator - chart: cozy-kubevirt-operator - namespace: cozy-kubevirt - dependsOn: [cilium,kubeovn,victoria-metrics-operator] - -- name: kubevirt - releaseName: kubevirt - chart: cozy-kubevirt - namespace: cozy-kubevirt - privileged: true - dependsOn: [cilium,kubeovn,kubevirt-operator] - {{- $cpuAllocationRatio := index $cozyConfig.data "cpu-allocation-ratio" }} - {{- if $cpuAllocationRatio }} - values: - cpuAllocationRatio: {{ $cpuAllocationRatio }} - {{- end }} - -- name: kubevirt-instancetypes - releaseName: kubevirt-instancetypes - chart: cozy-kubevirt-instancetypes - namespace: cozy-kubevirt - dependsOn: [cilium,kubeovn,kubevirt-operator,kubevirt] - -- name: kubevirt-cdi-operator - releaseName: kubevirt-cdi-operator - chart: cozy-kubevirt-cdi-operator - namespace: cozy-kubevirt-cdi - dependsOn: [cilium,kubeovn] - -- name: kubevirt-cdi - releaseName: kubevirt-cdi - chart: cozy-kubevirt-cdi - namespace: cozy-kubevirt-cdi - dependsOn: [cilium,kubeovn,kubevirt-cdi-operator] - -- name: gpu-operator - releaseName: gpu-operator - chart: cozy-gpu-operator - namespace: cozy-gpu-operator - privileged: true - optional: true - dependsOn: [cilium,kubeovn] - valuesFiles: - - values.yaml - - values-talos.yaml - -- name: metallb - releaseName: metallb - chart: cozy-metallb - namespace: cozy-metallb - privileged: true - dependsOn: [cilium,kubeovn] - -- name: etcd-operator - releaseName: etcd-operator - chart: cozy-etcd-operator - namespace: cozy-etcd-operator - dependsOn: [cilium,kubeovn,cert-manager] - -- name: grafana-operator - releaseName: grafana-operator - chart: cozy-grafana-operator - namespace: cozy-grafana-operator - dependsOn: [cilium,kubeovn] - -- name: mariadb-operator - releaseName: mariadb-operator - chart: cozy-mariadb-operator - namespace: cozy-mariadb-operator - dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator] - values: - mariadb-operator: - clusterName: {{ $clusterDomain }} - -- name: postgres-operator - releaseName: postgres-operator - chart: cozy-postgres-operator - namespace: cozy-postgres-operator - dependsOn: [cilium,kubeovn,cert-manager] - -- name: kafka-operator - releaseName: kafka-operator - chart: cozy-kafka-operator - namespace: cozy-kafka-operator - dependsOn: [cilium,kubeovn,victoria-metrics-operator] - values: - strimzi-kafka-operator: - kubernetesServiceDnsDomain: {{ $clusterDomain }} - -- name: clickhouse-operator - releaseName: clickhouse-operator - chart: cozy-clickhouse-operator - namespace: cozy-clickhouse-operator - dependsOn: [cilium,kubeovn,victoria-metrics-operator] - -- name: foundationdb-operator - releaseName: foundationdb-operator - chart: cozy-foundationdb-operator - namespace: cozy-foundationdb-operator - dependsOn: [cilium,kubeovn,cert-manager] - -- name: rabbitmq-operator - releaseName: rabbitmq-operator - chart: cozy-rabbitmq-operator - namespace: cozy-rabbitmq-operator - dependsOn: [cilium,kubeovn] - -- name: redis-operator - releaseName: redis-operator - chart: cozy-redis-operator - namespace: cozy-redis-operator - dependsOn: [cilium,kubeovn] - -- name: piraeus-operator - releaseName: piraeus-operator - chart: cozy-piraeus-operator - namespace: cozy-linstor - dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator] - -- name: linstor - releaseName: linstor - chart: cozy-linstor - namespace: cozy-linstor - privileged: true - dependsOn: [piraeus-operator,cilium,kubeovn,cert-manager,snapshot-controller] - -- name: nfs-driver - releaseName: nfs-driver - chart: cozy-nfs-driver - namespace: cozy-nfs-driver - privileged: true - dependsOn: [cilium,kubeovn] - optional: true - -- name: snapshot-controller - releaseName: snapshot-controller - chart: cozy-snapshot-controller - namespace: cozy-snapshot-controller - dependsOn: [cilium,kubeovn,cert-manager-issuers] - -- name: objectstorage-controller - releaseName: objectstorage-controller - chart: cozy-objectstorage-controller - namespace: cozy-objectstorage-controller - dependsOn: [cilium,kubeovn] - -- name: telepresence - releaseName: traffic-manager - chart: cozy-telepresence - namespace: cozy-telepresence - optional: true - dependsOn: [cilium,kubeovn] - -- name: dashboard - releaseName: dashboard - chart: cozy-dashboard - namespace: cozy-dashboard - values: - {{- $dashboardKCconfig := lookup "v1" "ConfigMap" "cozy-dashboard" "kubeapps-auth-config" }} - {{- $dashboardKCValues := dig "data" "values.yaml" "" $dashboardKCconfig | fromYaml }} - {{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }} - dependsOn: - - cilium - - kubeovn - {{- if eq $oidcEnabled "true" }} - - keycloak-configure - {{- end }} - -- name: kamaji - releaseName: kamaji - chart: cozy-kamaji - namespace: cozy-kamaji - dependsOn: [cilium,kubeovn,cert-manager] - -- name: capi-operator - releaseName: capi-operator - chart: cozy-capi-operator - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,cert-manager] - -- name: capi-providers-bootstrap - releaseName: capi-providers-bootstrap - chart: cozy-capi-providers-bootstrap - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,capi-operator] - -- name: capi-providers-core - releaseName: capi-providers-core - chart: cozy-capi-providers-core - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,capi-operator] - -- name: capi-providers-cpprovider - releaseName: capi-providers-cpprovider - chart: cozy-capi-providers-cpprovider - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,capi-operator] - -- name: capi-providers-infraprovider - releaseName: capi-providers-infraprovider - chart: cozy-capi-providers-infraprovider - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,capi-operator] - -- name: external-dns - releaseName: external-dns - chart: cozy-external-dns - namespace: cozy-external-dns - optional: true - dependsOn: [cilium,kubeovn] - -- name: external-secrets-operator - releaseName: external-secrets-operator - chart: cozy-external-secrets-operator - namespace: cozy-external-secrets-operator - optional: true - dependsOn: [cilium,kubeovn] - -- name: bootbox - releaseName: bootbox - chart: cozy-bootbox - namespace: cozy-bootbox - privileged: true - optional: true - dependsOn: [cilium,kubeovn] - -{{- if $oidcEnabled }} -- name: keycloak - releaseName: keycloak - chart: cozy-keycloak - namespace: cozy-keycloak - dependsOn: [postgres-operator] - -- name: keycloak-operator - releaseName: keycloak-operator - chart: cozy-keycloak-operator - namespace: cozy-keycloak - dependsOn: [keycloak] - -- name: keycloak-configure - releaseName: keycloak-configure - chart: cozy-keycloak-configure - namespace: cozy-keycloak - dependsOn: [keycloak-operator] - values: - cozystack: - configHash: {{ $cozyConfig | toJson | sha256sum }} -{{- end }} - -- name: goldpinger - releaseName: goldpinger - chart: cozy-goldpinger - namespace: cozy-goldpinger - privileged: true - dependsOn: [monitoring-agents] - -- name: vertical-pod-autoscaler - releaseName: vertical-pod-autoscaler - chart: cozy-vertical-pod-autoscaler - namespace: cozy-vertical-pod-autoscaler - privileged: true - dependsOn: [monitoring-agents] - values: - vertical-pod-autoscaler: - recommender: - extraArgs: - prometheus-address: http://vmselect-shortterm.tenant-root.svc.{{ $clusterDomain }}:8481/select/0/prometheus/ - -- name: vertical-pod-autoscaler-crds - releaseName: vertical-pod-autoscaler-crds - chart: cozy-vertical-pod-autoscaler-crds - namespace: cozy-vertical-pod-autoscaler - privileged: true - dependsOn: [cilium, kubeovn] - -- name: reloader - releaseName: reloader - chart: cozy-reloader - namespace: cozy-reloader - -- name: velero - releaseName: velero - chart: cozy-velero - namespace: cozy-velero - privileged: true - optional: true - dependsOn: [monitoring-agents] - -- name: hetzner-robotlb - releaseName: robotlb - optional: true - chart: cozy-hetzner-robotlb - namespace: cozy-hetzner-robotlb - dependsOn: [cilium, kubeovn] diff --git a/packages/core/platform/bundles/paas-hosted.yaml b/packages/core/platform/bundles/paas-hosted.yaml deleted file mode 100644 index 560578c7..00000000 --- a/packages/core/platform/bundles/paas-hosted.yaml +++ /dev/null @@ -1,274 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} -{{- $oidcEnabled := index $cozyConfig.data "oidc-enabled" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- if not $host }} -{{- fail "ERROR need root-host in cozystack ConfigMap" }} -{{- end }} -{{- $apiServerEndpoint := index $cozyConfig.data "api-server-endpoint" }} -{{- if not $apiServerEndpoint }} -{{- fail "ERROR need api-server-endpoint in cozystack ConfigMap" }} -{{- end }} - -releases: -- name: fluxcd-operator - releaseName: fluxcd-operator - chart: cozy-fluxcd-operator - namespace: cozy-fluxcd - privileged: true - dependsOn: [] - -- name: fluxcd - releaseName: fluxcd - chart: cozy-fluxcd - namespace: cozy-fluxcd - dependsOn: [fluxcd-operator] - values: - flux-instance: - instance: - cluster: - domain: {{ $clusterDomain }} - -- name: cert-manager-crds - releaseName: cert-manager-crds - chart: cozy-cert-manager-crds - namespace: cozy-cert-manager - dependsOn: [] - -- name: cozystack-api - releaseName: cozystack-api - chart: cozy-cozystack-api - namespace: cozy-system - dependsOn: [cozystack-controller] - values: - cozystackAPI: - localK8sAPIEndpoint: - enabled: false - -- name: cozystack-controller - releaseName: cozystack-controller - chart: cozy-cozystack-controller - namespace: cozy-system - dependsOn: [] - {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} - values: - cozystackController: - disableTelemetry: true - {{- end }} - -- name: lineage-controller-webhook - releaseName: lineage-controller-webhook - chart: cozy-lineage-controller-webhook - namespace: cozy-system - dependsOn: [cozystack-controller,cert-manager] - -- name: cozystack-resource-definition-crd - releaseName: cozystack-resource-definition-crd - chart: cozystack-resource-definition-crd - namespace: cozy-system - dependsOn: [cozystack-api,cozystack-controller] - -- name: cozystack-resource-definitions - releaseName: cozystack-resource-definitions - chart: cozystack-resource-definitions - namespace: cozy-system - dependsOn: [cozystack-api,cozystack-controller,cozystack-resource-definition-crd] - -- name: cert-manager - releaseName: cert-manager - chart: cozy-cert-manager - namespace: cozy-cert-manager - dependsOn: [cert-manager-crds] - -- name: cert-manager-issuers - releaseName: cert-manager-issuers - chart: cozy-cert-manager-issuers - namespace: cozy-cert-manager - dependsOn: [cert-manager] - -- name: victoria-metrics-operator - releaseName: victoria-metrics-operator - chart: cozy-victoria-metrics-operator - namespace: cozy-victoria-metrics-operator - dependsOn: [cert-manager] - -- name: monitoring-agents - releaseName: monitoring-agents - chart: cozy-monitoring-agents - namespace: cozy-monitoring - privileged: true - dependsOn: [victoria-metrics-operator, vertical-pod-autoscaler-crds] - values: - scrapeRules: - etcd: - enabled: true - -- name: etcd-operator - releaseName: etcd-operator - chart: cozy-etcd-operator - namespace: cozy-etcd-operator - dependsOn: [cert-manager] - -- name: grafana-operator - releaseName: grafana-operator - chart: cozy-grafana-operator - namespace: cozy-grafana-operator - dependsOn: [] - -- name: mariadb-operator - releaseName: mariadb-operator - chart: cozy-mariadb-operator - namespace: cozy-mariadb-operator - dependsOn: [cert-manager,victoria-metrics-operator] - values: - mariadb-operator: - clusterName: {{ $clusterDomain }} - -- name: postgres-operator - releaseName: postgres-operator - chart: cozy-postgres-operator - namespace: cozy-postgres-operator - dependsOn: [cert-manager,victoria-metrics-operator] - -- name: kafka-operator - releaseName: kafka-operator - chart: cozy-kafka-operator - namespace: cozy-kafka-operator - dependsOn: [victoria-metrics-operator] - values: - strimzi-kafka-operator: - kubernetesServiceDnsDomain: {{ $clusterDomain }} - -- name: clickhouse-operator - releaseName: clickhouse-operator - chart: cozy-clickhouse-operator - namespace: cozy-clickhouse-operator - dependsOn: [victoria-metrics-operator] - -- name: foundationdb-operator - releaseName: foundationdb-operator - chart: cozy-foundationdb-operator - namespace: cozy-foundationdb-operator - dependsOn: [cert-manager] - -- name: rabbitmq-operator - releaseName: rabbitmq-operator - chart: cozy-rabbitmq-operator - namespace: cozy-rabbitmq-operator - dependsOn: [] - -- name: redis-operator - releaseName: redis-operator - chart: cozy-redis-operator - namespace: cozy-redis-operator - dependsOn: [] - -- name: piraeus-operator - releaseName: piraeus-operator - chart: cozy-piraeus-operator - namespace: cozy-linstor - dependsOn: [cert-manager] - -- name: objectstorage-controller - releaseName: objectstorage-controller - chart: cozy-objectstorage-controller - namespace: cozy-objectstorage-controller - dependsOn: [] - -- name: telepresence - releaseName: traffic-manager - chart: cozy-telepresence - namespace: cozy-telepresence - optional: true - dependsOn: [] - -- name: external-dns - releaseName: external-dns - chart: cozy-external-dns - namespace: cozy-external-dns - optional: true - dependsOn: [] - -- name: external-secrets-operator - releaseName: external-secrets-operator - chart: cozy-external-secrets-operator - namespace: cozy-external-secrets-operator - optional: true - dependsOn: [] - -- name: dashboard - releaseName: dashboard - chart: cozy-dashboard - namespace: cozy-dashboard - values: - {{- $dashboardKCconfig := lookup "v1" "ConfigMap" "cozy-dashboard" "kubeapps-auth-config" }} - {{- $dashboardKCValues := dig "data" "values.yaml" (dict) $dashboardKCconfig }} - {{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }} - {{- if eq $oidcEnabled "true" }} - dependsOn: [keycloak-configure,cozystack-api] - {{- else }} - dependsOn: [] - {{- end }} - -{{- if $oidcEnabled }} -- name: keycloak - releaseName: keycloak - chart: cozy-keycloak - namespace: cozy-keycloak - dependsOn: [postgres-operator] - -- name: keycloak-operator - releaseName: keycloak-operator - chart: cozy-keycloak-operator - namespace: cozy-keycloak - dependsOn: [keycloak] - -- name: keycloak-configure - releaseName: keycloak-configure - chart: cozy-keycloak-configure - namespace: cozy-keycloak - dependsOn: [keycloak-operator] - values: - cozystack: - configHash: {{ $cozyConfig | toJson | sha256sum }} -{{- end }} - -- name: goldpinger - releaseName: goldpinger - chart: cozy-goldpinger - namespace: cozy-goldpinger - privileged: true - dependsOn: [monitoring-agents] - -- name: vertical-pod-autoscaler - releaseName: vertical-pod-autoscaler - chart: cozy-vertical-pod-autoscaler - namespace: cozy-vertical-pod-autoscaler - privileged: true - dependsOn: [monitoring-agents] - values: - vertical-pod-autoscaler: - recommender: - extraArgs: - prometheus-address: http://vmselect-shortterm.tenant-root.svc.{{ $clusterDomain }}:8481/select/0/prometheus/ - -- name: vertical-pod-autoscaler-crds - releaseName: vertical-pod-autoscaler-crds - chart: cozy-vertical-pod-autoscaler-crds - namespace: cozy-vertical-pod-autoscaler - privileged: true - dependsOn: [] - -- name: velero - releaseName: velero - chart: cozy-velero - namespace: cozy-velero - privileged: true - optional: true - dependsOn: [monitoring-agents] - -- name: hetzner-robotlb - releaseName: robotlb - optional: true - chart: cozy-hetzner-robotlb - namespace: cozy-hetzner-robotlb diff --git a/packages/system/cozystack-resource-definitions/cozyrds/clickhouse.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/clickhouse.yaml similarity index 94% rename from packages/system/cozystack-resource-definitions/cozyrds/clickhouse.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/clickhouse.yaml index 6948fce0..746ceee7 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/clickhouse.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/clickhouse.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: clickhouse spec: @@ -13,12 +13,14 @@ spec: prefix: clickhouse- labels: cozystack.io/ui: "true" - chart: - name: clickhouse + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-clickhouse + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: ClickHouse @@ -31,9 +33,9 @@ spec: exclude: [] include: - resourceNames: - - clickhouse-{{ .name }}-credentials + - "{{`clickhouse-{{ .name }}-credentials`}}" services: exclude: [] include: - resourceNames: - - chendpoint-clickhouse-{{ .name }} + - "{{`chendpoint-clickhouse-{{ .name }}`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/ferretdb.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/ferretdb.yaml similarity index 96% rename from packages/system/cozystack-resource-definitions/cozyrds/ferretdb.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/ferretdb.yaml index f7bdeb82..31982d2c 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/ferretdb.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/ferretdb.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: ferretdb spec: @@ -13,12 +13,14 @@ spec: prefix: ferretdb- labels: cozystack.io/ui: "true" - chart: - name: ferretdb + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-ferretdb + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: FerretDB @@ -32,9 +34,9 @@ spec: exclude: [] include: - resourceNames: - - ferretdb-{{ .name }}-credentials + - "{{`ferretdb-{{ .name }}-credentials`}}" services: exclude: [] include: - resourceNames: - - ferretdb-{{ .name }} + - "{{`ferretdb-{{ .name }}`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/foundationdb.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/foundationdb.yaml similarity index 97% rename from packages/system/cozystack-resource-definitions/cozyrds/foundationdb.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/foundationdb.yaml index e7759380..9f2b6c67 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/foundationdb.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/foundationdb.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: foundationdb spec: @@ -13,12 +13,14 @@ spec: prefix: foundationdb- labels: cozystack.io/ui: "true" - chart: - name: foundationdb + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-foundationdb + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: FoundationDB @@ -27,4 +29,4 @@ spec: tags: - database icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX3JhZGlhbF84NThfMzA3MikiLz4KPHBhdGggZD0iTTEzNS43ODQgNzUuNjQ0NkwxMzUuOTM5IDg3Ljc2MzhMODkuNjg0NiA4MS41MzYyTDYyLjA4NjggODQuNTA3OUwzNS4zNDE3IDgxLjQzMjlMOC43NTE2NyA4NC41ODU0TDguNzI1ODMgODEuNTEwNEwzNS4zNjc2IDc3LjU4MjZWNjQuMTcxM0w2Mi4yOTM1IDcwLjczNDhMNjIuMzQ1MiA4MS4yNzc4TDg5LjQ3NzkgNzcuNjg2TDg5LjQwMDQgNjQuMTk3MkwxMzUuNzg0IDc1LjY0NDZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNODkuNDc3OCA4Ni4wMzI1TDEzNS44ODggOTAuODM4OFYxMDIuNzI2SDguNjQ4MjVMOC41MTkwNCA5OS41NzNIMzUuMjY0MUMzNS4yNjQxIDk5LjU3MyAzNS4yNjQxIDkwLjczNTUgMzUuMjY0MSA4Ni4wNTgzQzQ0LjI1NjcgODYuOTM2OSA2Mi4wODY3IDg4LjY5NDEgNjIuMDg2NyA4OC42OTQxVjk5LjI2MjlIODkuNDc3OFY4Ni4wMzI1WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTYyLjI5MzQgNjYuODg0Nkw2Mi4yMTU4IDYzLjYyODZDNjIuMjE1OCA2My42Mjg2IDc5LjgxMzMgNTguMzU3MSA4OC45MDkyIDU1LjY2OTdDODguOTA5MiA1MS4zMDI2IDg4LjkwOTIgNDcuMDkwNiA4OC45MDkyIDQyQzEwNC44NzkgNDguNDA4NSAxMjAuMjI4IDU0LjYxMDIgMTM1LjczMyA2MC44Mzc4QzEzNS43MzMgNjQuNzEzOSAxMzUuNzMzIDY4LjQzNSAxMzUuNzMzIDcyLjU2OTVDMTE5Ljg0MSA2OC4yMDI0IDEwNC4yODQgNjMuOTEyOSA4OS4xNjc2IDU5Ljc1MjVDNzkuOTY4NCA2Mi4yMDc0IDYyLjI5MzQgNjYuODg0NiA2Mi4yOTM0IDY2Ljg4NDZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzUuMzk2MiA4MS43MDczTDguODA2MTIgODQuODU5OEw4Ljc4MDI3IDgxLjc4NDhMMzUuNDIyIDc3Ljg1N1Y2NC40NDU3TDYyLjM0OCA3MS4wMDkzTDYyLjM5OTYgODEuNTUyMkw4OS41MzIzIDc3Ljk2MDRMODkuNDU0OCA2NC40NzE2TDEzNS44MzkgNzUuOTE5TDEzNS45OTQgODguMDM4Mkw4OS43MzkxIDgxLjgxMDZMNjIuMTQxMiA4NC43ODIzTDM1LjM5NjIgODEuNzA3M1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik04OS41MzIzIDg2LjMwNjlMMTM1Ljk0MiA5MS4xMTMzVjEwM0g4LjcwMjdMOC41NzM0OSA5OS44NDc0SDM1LjMxODZDMzUuMzE4NiA5OS44NDc0IDM1LjMxODYgOTEuMDA5OSAzNS4zMTg2IDg2LjMzMjhDNDQuMzExMSA4Ny4yMTE0IDYyLjE0MTIgODguOTY4NSA2Mi4xNDEyIDg4Ljk2ODVWOTkuNTM3M0g4OS41MzIzVjg2LjMwNjlaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNjIuMzQ4MyA2Ny4xNTlMNjIuMjcwOCA2My45MDMxQzYyLjI3MDggNjMuOTAzMSA3OS44NjgyIDU4LjYzMTYgODguOTY0MiA1NS45NDQyQzg4Ljk2NDIgNTEuNTc3MSA4OC45NjQyIDQ3LjM2NTEgODguOTY0MiA0Mi4yNzQ0QzEwNC45MzQgNDguNjgyOSAxMjAuMjgzIDU0Ljg4NDcgMTM1Ljc4NyA2MS4xMTIzQzEzNS43ODcgNjQuOTg4NCAxMzUuNzg3IDY4LjcwOTQgMTM1Ljc4NyA3Mi44NDM5QzExOS44OTUgNjguNDc2OSAxMDQuMzM5IDY0LjE4NzMgODkuMjIyNiA2MC4wMjdDODAuMDIzMyA2Mi40ODE4IDYyLjM0ODMgNjcuMTU5IDYyLjM0ODMgNjcuMTU5WiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQwX3JhZGlhbF84NThfMzA3MiIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtMjkuNSAtMTgpIHJvdGF0ZSgzOS42OTYzKSBzY2FsZSgzMDIuMTY4IDI3NS4yNzEpIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0JFRERGRiIvPgo8c3RvcCBvZmZzZXQ9IjAuMjU5NjE1IiBzdG9wLWNvbG9yPSIjOUVDQ0ZEIi8+CjxzdG9wIG9mZnNldD0iMC41OTEzNDYiIHN0b3AtY29sb3I9IiMzRjlBRkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMEI3MEUwIi8+CjwvcmFkaWFsR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== - # keysOrder: [] + # keysOrder: [] diff --git a/packages/system/cozystack-resource-definitions/cozyrds/kafka.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/kafka.yaml similarity index 96% rename from packages/system/cozystack-resource-definitions/cozyrds/kafka.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/kafka.yaml index 7009d241..b369afae 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/kafka.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/kafka.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: kafka spec: @@ -13,12 +13,14 @@ spec: prefix: kafka- labels: cozystack.io/ui: "true" - chart: - name: kafka + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-kafka + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: Kafka @@ -32,9 +34,9 @@ spec: exclude: [] include: - resourceNames: - - kafka-{{ .name }}-clients-ca + - "{{`kafka-{{ .name }}-clients-ca`}}" services: exclude: [] include: - resourceNames: - - kafka-{{ .name }}-kafka-bootstrap + - "{{`kafka-{{ .name }}-kafka-bootstrap`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/mysql.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/mysql.yaml similarity index 96% rename from packages/system/cozystack-resource-definitions/cozyrds/mysql.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/mysql.yaml index 142b55b1..ac2b0628 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/mysql.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/mysql.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: mysql spec: @@ -13,12 +13,14 @@ spec: prefix: mysql- labels: cozystack.io/ui: "true" - chart: - name: mysql + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-mysql + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: MySQL @@ -32,10 +34,10 @@ spec: exclude: [] include: - resourceNames: - - mysql-{{ .name }}-credentials + - "{{`mysql-{{ .name }}-credentials`}}" services: exclude: [] include: - resourceNames: - - mysql-{{ .name }}-primary - - mysql-{{ .name }}-secondary + - "{{`mysql-{{ .name }}-primary`}}" + - "{{`mysql-{{ .name }}-secondary`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/nats.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/nats.yaml similarity index 93% rename from packages/system/cozystack-resource-definitions/cozyrds/nats.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/nats.yaml index 258f8f40..8dbe01e8 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/nats.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/nats.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: nats spec: @@ -13,12 +13,14 @@ spec: prefix: nats- labels: cozystack.io/ui: "true" - chart: - name: nats + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-nats + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: NATS @@ -32,9 +34,9 @@ spec: exclude: [] include: - resourceNames: - - nats-{{ .name }}-credentials + - "{{`nats-{{ .name }}-credentials`}}" services: exclude: [] include: - resourceNames: - - nats-{{ .name }} + - "{{`nats-{{ .name }}`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/postgres.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/postgres.yaml similarity index 96% rename from packages/system/cozystack-resource-definitions/cozyrds/postgres.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/postgres.yaml index 010586fe..f213b21c 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/postgres.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/postgres.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: postgres spec: @@ -13,12 +13,14 @@ spec: prefix: postgres- labels: cozystack.io/ui: "true" - chart: - name: postgres + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-postgres + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: PostgreSQL @@ -40,12 +42,12 @@ spec: exclude: [] include: - resourceNames: - - postgres-{{ .name }}-credentials + - "{{`postgres-{{ .name }}-credentials`}}" services: exclude: [] include: - resourceNames: - - postgres-{{ .name }}-r - - postgres-{{ .name }}-ro - - postgres-{{ .name }}-rw - - postgres-{{ .name }}-external-write + - "{{`postgres-{{ .name }}-r`}}" + - "{{`postgres-{{ .name }}-ro`}}" + - "{{`postgres-{{ .name }}-rw`}}" + - "{{`postgres-{{ .name }}-external-write`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/rabbitmq.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/rabbitmq.yaml similarity index 93% rename from packages/system/cozystack-resource-definitions/cozyrds/rabbitmq.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/rabbitmq.yaml index 092142ac..3046a089 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/rabbitmq.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/rabbitmq.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: rabbitmq spec: @@ -13,12 +13,14 @@ spec: prefix: rabbitmq- labels: cozystack.io/ui: "true" - chart: - name: rabbitmq + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-rabbitmq + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: RabbitMQ @@ -32,11 +34,11 @@ spec: exclude: [] include: - resourceNames: - - rabbitmq-{{ .name }}-default-user + - "{{`rabbitmq-{{ .name }}-default-user`}}" - matchLabels: apps.cozystack.io/user-secret: "true" services: exclude: [] include: - resourceNames: - - rabbitmq-{{ .name }} + - "{{`rabbitmq-{{ .name }}`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/redis.yaml b/packages/core/platform/bundles/paas/applicationdefinitions/redis.yaml similarity index 94% rename from packages/system/cozystack-resource-definitions/cozyrds/redis.yaml rename to packages/core/platform/bundles/paas/applicationdefinitions/redis.yaml index 1c5131d3..27d748e1 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/redis.yaml +++ b/packages/core/platform/bundles/paas/applicationdefinitions/redis.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: redis spec: @@ -13,12 +13,14 @@ spec: prefix: redis- labels: cozystack.io/ui: "true" - chart: - name: redis + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-paas-redis + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: PaaS singular: Redis @@ -32,12 +34,12 @@ spec: exclude: [] include: - resourceNames: - - redis-{{ .name }}-auth + - "{{`redis-{{ .name }}-auth`}}" services: exclude: [] include: - resourceNames: - - rfs-redis-{{ .name }} - - rfrm-redis-{{ .name }} - - rfrs-redis-{{ .name }} - - redis-{{ .name }}-external-lb + - "{{`rfs-redis-{{ .name }}`}}" + - "{{`rfrm-redis-{{ .name }}`}}" + - "{{`rfrs-redis-{{ .name }}`}}" + - "{{`redis-{{ .name }}-external-lb`}}" diff --git a/packages/core/platform/bundles/paas/bundle.yaml b/packages/core/platform/bundles/paas/bundle.yaml new file mode 100644 index 00000000..ac31d462 --- /dev/null +++ b/packages/core/platform/bundles/paas/bundle.yaml @@ -0,0 +1,100 @@ +{{- $clusterDomain := .Values.networking.clusterDomain | default "cozy.local" }} + +apiVersion: cozystack.io/v1alpha1 +kind: Bundle +metadata: + name: cozystack-paas +spec: + + dependsOn: [cozystack-system/network] + + sourceRef: + kind: {{ .Values.sourceRef.kind }} + name: {{ .Values.sourceRef.name }} + namespace: {{ .Values.sourceRef.namespace }} + + libraries: + - name: cozy-lib + path: library/cozy-lib + + artifacts: + - name: clickhouse + path: apps/clickhouse + libraries: [cozy-lib] + + - name: ferretdb + path: apps/ferretdb + libraries: [cozy-lib] + + - name: foundationdb + path: apps/foundationdb + libraries: [cozy-lib] + + - name: kafka + path: apps/kafka + libraries: [cozy-lib] + + - name: mysql + path: apps/mysql + libraries: [cozy-lib] + + - name: nats + path: apps/nats + libraries: [cozy-lib] + - name: nats-system + path: system/nats + + - name: postgres + path: apps/postgres + libraries: [cozy-lib] + + - name: rabbitmq + path: apps/rabbitmq + libraries: [cozy-lib] + + - name: redis + path: apps/redis + libraries: [cozy-lib] + + packages: + - name: mariadb-operator + releaseName: mariadb-operator + path: system/mariadb-operator + namespace: cozy-mariadb-operator + dependsOn: [cert-manager,victoria-metrics-operator] + values: + mariadb-operator: + clusterName: {{ $clusterDomain }} + + - name: kafka-operator + releaseName: kafka-operator + path: system/kafka-operator + namespace: cozy-kafka-operator + dependsOn: [victoria-metrics-operator] + values: + strimzi-kafka-operator: + kubernetesServiceDnsDomain: {{ $clusterDomain }} + + - name: clickhouse-operator + releaseName: clickhouse-operator + path: system/clickhouse-operator + namespace: cozy-clickhouse-operator + dependsOn: [victoria-metrics-operator] + + - name: foundationdb-operator + releaseName: foundationdb-operator + path: system/foundationdb-operator + namespace: cozy-foundationdb-operator + dependsOn: [cert-manager] + + - name: rabbitmq-operator + releaseName: rabbitmq-operator + path: system/rabbitmq-operator + namespace: cozy-rabbitmq-operator + dependsOn: [] + + - name: redis-operator + releaseName: redis-operator + path: system/redis-operator + namespace: cozy-redis-operator + dependsOn: [] diff --git a/packages/system/cozystack-resource-definitions/cozyrds/bootbox.yaml b/packages/core/platform/bundles/system/applicationdefinitions/bootbox.yaml similarity index 98% rename from packages/system/cozystack-resource-definitions/cozyrds/bootbox.yaml rename to packages/core/platform/bundles/system/applicationdefinitions/bootbox.yaml index f47bf8f6..7611944b 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/bootbox.yaml +++ b/packages/core/platform/bundles/system/applicationdefinitions/bootbox.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: bootbox spec: @@ -13,12 +13,14 @@ spec: prefix: "" labels: cozystack.io/ui: "true" - chart: - name: bootbox + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-system-bootbox + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: Administration singular: BootBox diff --git a/packages/system/cozystack-resource-definitions/cozyrds/etcd.yaml b/packages/core/platform/bundles/system/applicationdefinitions/etcd.yaml similarity index 96% rename from packages/system/cozystack-resource-definitions/cozyrds/etcd.yaml rename to packages/core/platform/bundles/system/applicationdefinitions/etcd.yaml index 7381469d..ab30e50b 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/etcd.yaml +++ b/packages/core/platform/bundles/system/applicationdefinitions/etcd.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: etcd spec: @@ -14,12 +14,14 @@ spec: labels: cozystack.io/ui: "true" internal.cozystack.io/tenantmodule: "true" - chart: - name: etcd + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-system-etcd + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: Administration singular: Etcd diff --git a/packages/system/cozystack-resource-definitions/cozyrds/info.yaml b/packages/core/platform/bundles/system/applicationdefinitions/info.yaml similarity index 88% rename from packages/system/cozystack-resource-definitions/cozyrds/info.yaml rename to packages/core/platform/bundles/system/applicationdefinitions/info.yaml index 43cda091..8e53ea46 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/info.yaml +++ b/packages/core/platform/bundles/system/applicationdefinitions/info.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: info spec: @@ -14,12 +14,14 @@ spec: labels: cozystack.io/ui: "true" internal.cozystack.io/tenantmodule: "true" - chart: - name: info + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-system-info + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: name: info category: Administration @@ -33,5 +35,5 @@ spec: exclude: [] include: - resourceNames: - - kubeconfig-{{ .namespace }} - - "{{ .namespace }}" + - "{{`kubeconfig-{{ .namespace }}`}}" + - "{{`{{ .namespace }}`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/ingress.yaml b/packages/core/platform/bundles/system/applicationdefinitions/ingress.yaml similarity index 93% rename from packages/system/cozystack-resource-definitions/cozyrds/ingress.yaml rename to packages/core/platform/bundles/system/applicationdefinitions/ingress.yaml index 2e00f6ed..9f720eab 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/ingress.yaml +++ b/packages/core/platform/bundles/system/applicationdefinitions/ingress.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: ingress spec: @@ -14,12 +14,14 @@ spec: labels: cozystack.io/ui: "true" internal.cozystack.io/tenantmodule: "true" - chart: - name: ingress + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-system-ingress + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: Administration singular: Ingress @@ -36,4 +38,4 @@ spec: exclude: [] include: - resourceNames: - - "{{ slice .namespace 7 }}-ingress-controller" + - "{{`{{ slice .namespace 7 }}-ingress-controller`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/monitoring.yaml b/packages/core/platform/bundles/system/applicationdefinitions/monitoring.yaml similarity index 98% rename from packages/system/cozystack-resource-definitions/cozyrds/monitoring.yaml rename to packages/core/platform/bundles/system/applicationdefinitions/monitoring.yaml index 520e4671..3e9aa156 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/monitoring.yaml +++ b/packages/core/platform/bundles/system/applicationdefinitions/monitoring.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: monitoring spec: @@ -14,12 +14,14 @@ spec: labels: cozystack.io/ui: "true" internal.cozystack.io/tenantmodule: "true" - chart: - name: monitoring + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-system-monitoring + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: Administration singular: Monitoring diff --git a/packages/system/cozystack-resource-definitions/cozyrds/seaweedfs.yaml b/packages/core/platform/bundles/system/applicationdefinitions/seaweedfs.yaml similarity index 99% rename from packages/system/cozystack-resource-definitions/cozyrds/seaweedfs.yaml rename to packages/core/platform/bundles/system/applicationdefinitions/seaweedfs.yaml index 787b5448..04312f9f 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/seaweedfs.yaml +++ b/packages/core/platform/bundles/system/applicationdefinitions/seaweedfs.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: seaweedfs spec: @@ -14,12 +14,14 @@ spec: labels: cozystack.io/ui: "true" internal.cozystack.io/tenantmodule: "true" - chart: - name: seaweedfs + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-system-seaweedfs + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: Administration singular: SeaweedFS @@ -36,9 +38,9 @@ spec: exclude: [] include: - resourceNames: - - seaweedfs-{{ .name }}-s3 + - "{{`seaweedfs-{{ .name }}-s3`}}" ingresses: exclude: [] include: - resourceNames: - - ingress-seaweedfs-{{ .name }}-s3 + - "{{`ingress-seaweedfs-{{ .name }}-s3`}}" diff --git a/packages/system/cozystack-resource-definitions/cozyrds/tenant.yaml b/packages/core/platform/bundles/system/applicationdefinitions/tenant.yaml similarity index 93% rename from packages/system/cozystack-resource-definitions/cozyrds/tenant.yaml rename to packages/core/platform/bundles/system/applicationdefinitions/tenant.yaml index a5c497ac..a1876ea7 100644 --- a/packages/system/cozystack-resource-definitions/cozyrds/tenant.yaml +++ b/packages/core/platform/bundles/system/applicationdefinitions/tenant.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: tenant spec: @@ -13,12 +13,14 @@ spec: prefix: tenant- labels: cozystack.io/ui: "true" - chart: - name: tenant + chartRef: sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-system-tenant + namespace: cozy-system + values: + _cozystack: + {{- include "cozystack.build-values" . | nindent 8 }} dashboard: category: Administration singular: Tenant diff --git a/packages/core/platform/bundles/system/bundle-full.yaml b/packages/core/platform/bundles/system/bundle-full.yaml new file mode 100644 index 00000000..01d9dbb0 --- /dev/null +++ b/packages/core/platform/bundles/system/bundle-full.yaml @@ -0,0 +1,401 @@ +{{- $host := .Values.publishing.host }} +{{- if not $host }} +{{- fail "ERROR: publishing.host is required" }} +{{- end }} +{{- $clusterDomain := .Values.networking.clusterDomain | default "cozy.local" }} +{{- $oidcEnabled := .Values.authentication.oidc.enabled | default false }} + +apiVersion: cozystack.io/v1alpha1 +kind: Bundle +metadata: + name: cozystack-system +spec: + deletionPolicy: Orphan + + dependencyTargets: + - name: network + packages: [cilium,kubeovn,cert-manager] + + sourceRef: + kind: {{ .Values.sourceRef.kind }} + name: {{ .Values.sourceRef.name }} + namespace: {{ .Values.sourceRef.namespace }} + + libraries: + - name: cozy-lib + path: library/cozy-lib + + artifacts: + - name: tenant + path: apps/tenant + libraries: [cozy-lib] + - name: bootbox + path: extra/bootbox + libraries: [cozy-lib] + - name: etcd + path: extra/etcd + libraries: [cozy-lib] + - name: ingress + path: extra/ingress + libraries: [cozy-lib] + - name: ingress-system + path: system/ingress-nginx + - name: seaweedfs + path: extra/seaweedfs + libraries: [cozy-lib] + - name: seaweedfs-system + path: system/seaweedfs + - name: info + path: extra/info + libraries: [cozy-lib] + - name: monitoring + path: extra/monitoring + libraries: [cozy-lib] + + packages: + - name: tenant-root + releaseName: tenant-root + artifact: tenant + namespace: tenant-root + namespaceAnnotations: + namespace.cozystack.io/host: {{ $host }} + namespace.cozystack.io/etcd: tenant-root + namespace.cozystack.io/ingress: tenant-root + namespace.cozystack.io/monitoring: tenant-root + namespace.cozystack.io/seaweedfs: tenant-root + namespaceLabels: + namespace.cozystack.io/etcd: tenant-root + namespace.cozystack.io/ingress: tenant-root + namespace.cozystack.io/monitoring: tenant-root + namespace.cozystack.io/seaweedfs: tenant-root + labels: + cozystack.io/ui: "true" + apps.cozystack.io/application.kind: Tenant + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: tenant-root + values: + host: {{ $host }} + + - name: cilium + releaseName: cilium + path: system/cilium + namespace: cozy-cilium + privileged: true + dependsOn: [] + valuesFiles: + - values.yaml + - values-talos.yaml + - values-kubeovn.yaml + + - name: cilium-networkpolicy + releaseName: cilium-networkpolicy + path: system/cilium-networkpolicy + namespace: cozy-cilium + privileged: true + dependsOn: [cilium] + + - name: kubeovn + releaseName: kubeovn + path: system/kubeovn + namespace: cozy-kubeovn + privileged: true + dependsOn: [cilium] + values: + cozystack: + nodesHash: {{ include "cozystack.master-node-ips" . | sha256sum }} + kube-ovn: + ipv4: + POD_CIDR: "{{ .Values.networking.podCIDR }}" + POD_GATEWAY: "{{ .Values.networking.podGateway }}" + SVC_CIDR: "{{ .Values.networking.serviceCIDR }}" + JOIN_CIDR: "{{ .Values.networking.joinCIDR }}" + + - name: kubeovn-webhook + releaseName: kubeovn-webhook + path: system/kubeovn-webhook + namespace: cozy-kubeovn + privileged: true + dependsOn: [cilium,kubeovn,cert-manager] + + - name: kubeovn-plunger + releaseName: kubeovn-plunger + path: system/kubeovn-plunger + namespace: cozy-kubeovn + dependsOn: [cilium,kubeovn,victoria-metrics-operator] + + - name: multus + releaseName: multus + path: system/multus + namespace: cozy-multus + privileged: true + dependsOn: [cilium,kubeovn] + + - name: cozy-proxy + releaseName: cozystack + path: system/cozy-proxy + namespace: cozy-system + dependsOn: [cilium,kubeovn] + + - name: cert-manager-crds + releaseName: cert-manager-crds + path: system/cert-manager-crds + namespace: cozy-cert-manager + dependsOn: [cilium, kubeovn] + + - name: cozystack-api + releaseName: cozystack-api + path: system/cozystack-api + namespace: cozy-system + dependsOn: [cilium,kubeovn,cozystack-controller] + values: + cozystack: + publishing: + host: {{ $host }} + exposedServices: {{ .Values.publishing.exposedServices | default list | toJson }} + ingressClassName: {{ .Values.publishing.ingressName | default "tenant-root" }} + + - name: cozystack-controller + releaseName: cozystack-controller + path: system/cozystack-controller + namespace: cozy-system + dependsOn: [cilium,kubeovn] + + - name: lineage-controller-webhook + releaseName: lineage-controller-webhook + path: system/lineage-controller-webhook + namespace: cozy-system + dependsOn: [cozystack-controller,cilium,kubeovn,cert-manager] + + - name: cert-manager + releaseName: cert-manager + path: system/cert-manager + namespace: cozy-cert-manager + dependsOn: [cert-manager-crds] + + - name: cert-manager-issuers + releaseName: cert-manager-issuers + path: system/cert-manager-issuers + namespace: cozy-cert-manager + dependsOn: [cilium,kubeovn,cert-manager] + + - name: victoria-metrics-operator + releaseName: victoria-metrics-operator + path: system/victoria-metrics-operator + namespace: cozy-victoria-metrics-operator + dependsOn: [cilium,kubeovn,cert-manager] + + - name: monitoring-agents + releaseName: monitoring-agents + path: system/monitoring-agents + namespace: cozy-monitoring + privileged: true + dependsOn: [victoria-metrics-operator, vertical-pod-autoscaler-crds] + values: + scrapeRules: + etcd: + enabled: true + + - name: metallb + releaseName: metallb + path: system/metallb + namespace: cozy-metallb + privileged: true + dependsOn: [cilium,kubeovn] + + - name: etcd-operator + releaseName: etcd-operator + path: system/etcd-operator + namespace: cozy-etcd-operator + dependsOn: [cilium,kubeovn,cert-manager] + + - name: grafana-operator + releaseName: grafana-operator + path: system/grafana-operator + namespace: cozy-grafana-operator + dependsOn: [cilium,kubeovn] + + - name: postgres-operator + releaseName: postgres-operator + path: system/postgres-operator + namespace: cozy-postgres-operator + dependsOn: [cilium,kubeovn,cert-manager] + + - name: piraeus-operator + releaseName: piraeus-operator + path: system/piraeus-operator + namespace: cozy-linstor + dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator] + + - name: linstor + releaseName: linstor + path: system/linstor + namespace: cozy-linstor + privileged: true + dependsOn: [piraeus-operator,cilium,kubeovn,cert-manager,snapshot-controller] + + - name: nfs-driver + releaseName: nfs-driver + path: system/nfs-driver + namespace: cozy-nfs-driver + privileged: true + dependsOn: [cilium,kubeovn] + disabled: true + + - name: snapshot-controller + releaseName: snapshot-controller + path: system/snapshot-controller + namespace: cozy-snapshot-controller + dependsOn: [cilium,kubeovn,cert-manager-issuers] + + - name: objectstorage-controller + releaseName: objectstorage-controller + path: system/objectstorage-controller + namespace: cozy-objectstorage-controller + dependsOn: [cilium,kubeovn] + + - name: telepresence + releaseName: traffic-manager + path: system/telepresence + namespace: cozy-telepresence + disabled: true + dependsOn: [cilium,kubeovn] + + - name: dashboard + releaseName: dashboard + path: system/dashboard + namespace: cozy-dashboard + dependsOn: + - cilium + - kubeovn + - cozystack-api + - cozystack-controller + {{- if $oidcEnabled }} + - keycloak-configure + {{- end }} + values: + cozystack: + publishing: + host: {{ $host }} + ingressClassName: {{ .Values.publishing.ingressName | default "tenant-root" }} + exposedServices: {{ .Values.publishing.exposedServices | toJson }} + certificates: + issuerType: {{ .Values.publishing.certificates.issuerType | default "http01" }} + authentication: + oidc: + enabled: {{ $oidcEnabled }} + dashboard: + extraRedirectUris: {{ .Values.authentication.oidc.dashboard.extraRedirectUris | toJson }} + {{- if hasKey .Values.branding "dashboard" }} + branding: + dashboard: + {{- toYaml .Values.branding.dashboard | nindent 14 }} + {{- end }} + + - name: external-dns + releaseName: external-dns + path: system/external-dns + namespace: cozy-external-dns + disabled: true + dependsOn: [cilium,kubeovn] + + - name: external-secrets-operator + releaseName: external-secrets-operator + path: system/external-secrets-operator + namespace: cozy-external-secrets-operator + disabled: true + dependsOn: [cilium,kubeovn] + + - name: bootbox + releaseName: bootbox + path: system/bootbox + namespace: cozy-bootbox + privileged: true + disabled: true + dependsOn: [cilium,kubeovn] + + {{- if $oidcEnabled }} + - name: keycloak + releaseName: keycloak + path: system/keycloak + namespace: cozy-keycloak + dependsOn: [postgres-operator] + libraries: [cozy-lib] + values: + cozystack: + publishing: + host: {{ $host }} + ingressClassName: {{ .Values.publishing.ingressName | default "tenant-root" }} + certificates: + issuerType: {{ .Values.publishing.certificates.issuerType | default "http01" }} + networking: + clusterDomain: {{ $clusterDomain }} + scheduling: + topologySpreadConstraints: {{ .Values.scheduling.topologySpreadConstraints | default list | toJson }} + + - name: keycloak-operator + releaseName: keycloak-operator + path: system/keycloak-operator + namespace: cozy-keycloak + dependsOn: [keycloak] + + - name: keycloak-configure + releaseName: keycloak-configure + path: system/keycloak-configure + namespace: cozy-keycloak + dependsOn: [keycloak-operator] + values: + cozystack: + publishing: + host: {{ $host }} + {{- if hasKey .Values.branding "keycloak" }} + branding: + {{- toYaml .Values.branding.keycloak | nindent 12 }} + {{- end }} + + {{- end }} + + - name: goldpinger + releaseName: goldpinger + path: system/goldpinger + namespace: cozy-goldpinger + privileged: true + dependsOn: [monitoring-agents] + + - name: vertical-pod-autoscaler + releaseName: vertical-pod-autoscaler + path: system/vertical-pod-autoscaler + namespace: cozy-vertical-pod-autoscaler + privileged: true + dependsOn: [monitoring-agents] + values: + vertical-pod-autoscaler: + recommender: + extraArgs: + prometheus-address: http://vmselect-shortterm.tenant-root.svc.{{ $clusterDomain }}:8481/select/0/prometheus/ + + - name: vertical-pod-autoscaler-crds + releaseName: vertical-pod-autoscaler-crds + path: system/vertical-pod-autoscaler-crds + namespace: cozy-vertical-pod-autoscaler + privileged: true + dependsOn: [cilium, kubeovn] + + - name: reloader + releaseName: reloader + path: system/reloader + namespace: cozy-reloader + + - name: velero + releaseName: velero + path: system/velero + namespace: cozy-velero + privileged: true + disabled: true + dependsOn: [monitoring-agents] + + - name: hetzner-robotlb + releaseName: robotlb + disabled: true + path: system/hetzner-robotlb + namespace: cozy-hetzner-robotlb + dependsOn: [cilium, kubeovn] diff --git a/packages/core/platform/bundles/system/bundle-hosted.yaml b/packages/core/platform/bundles/system/bundle-hosted.yaml new file mode 100644 index 00000000..41f485aa --- /dev/null +++ b/packages/core/platform/bundles/system/bundle-hosted.yaml @@ -0,0 +1,303 @@ +{{- $host := .Values.publishing.host }} +{{- if not $host }} +{{- fail "ERROR: publishing.host is required" }} +{{- end }} +{{- $clusterDomain := .Values.networking.clusterDomain | default "cozy.local" }} +{{- $oidcEnabled := .Values.authentication.oidc.enabled | default false }} + +apiVersion: cozystack.io/v1alpha1 +kind: Bundle +metadata: + name: cozystack-system +spec: + deletionPolicy: Orphan + + dependencyTargets: + - name: network + packages: [] + + sourceRef: + kind: {{ .Values.sourceRef.kind }} + name: {{ .Values.sourceRef.name }} + namespace: {{ .Values.sourceRef.namespace }} + + libraries: + - name: cozy-lib + path: library/cozy-lib + + artifacts: + - name: tenant + path: apps/tenant + libraries: [cozy-lib] + - name: bootbox + path: extra/bootbox + libraries: [cozy-lib] + - name: etcd + path: extra/etcd + libraries: [cozy-lib] + - name: ingress + path: extra/ingress + libraries: [cozy-lib] + - name: ingress-system + path: system/ingress + - name: seaweedfs + path: extra/seaweedfs + libraries: [cozy-lib] + - name: seaweedfs-system + path: system/seaweedfs + - name: info + path: extra/info + libraries: [cozy-lib] + - name: monitoring + path: extra/monitoring + libraries: [cozy-lib] + + packages: + - name: tenant-root + releaseName: tenant-root + artifact: tenant + namespace: tenant-root + namespaceAnnotations: + namespace.cozystack.io/host: {{ $host }} + namespace.cozystack.io/etcd: tenant-root + namespace.cozystack.io/ingress: tenant-root + namespace.cozystack.io/monitoring: tenant-root + namespace.cozystack.io/seaweedfs: tenant-root + namespaceLabels: + namespace.cozystack.io/etcd: tenant-root + namespace.cozystack.io/ingress: tenant-root + namespace.cozystack.io/monitoring: tenant-root + namespace.cozystack.io/seaweedfs: tenant-root + labels: + cozystack.io/ui: "true" + apps.cozystack.io/application.kind: Tenant + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: tenant-root + values: + host: {{ $host }} + + - name: cert-manager-crds + releaseName: cert-manager-crds + path: system/cert-manager-crds + namespace: cozy-cert-manager + dependsOn: [] + + - name: cozystack-api + releaseName: cozystack-api + path: system/cozystack-api + namespace: cozy-system + dependsOn: [cozystack-controller] + values: + cozystackAPI: + localK8sAPIEndpoint: + enabled: false + + - name: cozystack-controller + releaseName: cozystack-controller + path: system/cozystack-controller + namespace: cozy-system + dependsOn: [] + + - name: lineage-controller-webhook + releaseName: lineage-controller-webhook + path: system/lineage-controller-webhook + namespace: cozy-system + dependsOn: [cozystack-controller,cert-manager] + + - name: cert-manager + releaseName: cert-manager + path: system/cert-manager + namespace: cozy-cert-manager + dependsOn: [cert-manager-crds] + + - name: cert-manager-issuers + releaseName: cert-manager-issuers + path: system/cert-manager-issuers + namespace: cozy-cert-manager + dependsOn: [cert-manager] + + - name: victoria-metrics-operator + releaseName: victoria-metrics-operator + path: system/victoria-metrics-operator + namespace: cozy-victoria-metrics-operator + dependsOn: [cert-manager] + + - name: monitoring-agents + releaseName: monitoring-agents + path: system/monitoring-agents + namespace: cozy-monitoring + privileged: true + dependsOn: [victoria-metrics-operator, vertical-pod-autoscaler-crds] + values: + scrapeRules: + etcd: + enabled: true + + - name: etcd-operator + releaseName: etcd-operator + path: system/etcd-operator + namespace: cozy-etcd-operator + dependsOn: [cert-manager] + + - name: grafana-operator + releaseName: grafana-operator + path: system/grafana-operator + namespace: cozy-grafana-operator + dependsOn: [] + + - name: mariadb-operator + releaseName: mariadb-operator + path: system/mariadb-operator + namespace: cozy-mariadb-operator + dependsOn: [cert-manager,victoria-metrics-operator] + values: + mariadb-operator: + clusterName: {{ .Values.networking.clusterDomain | default "cozy.local" }} + + - name: postgres-operator + releaseName: postgres-operator + path: system/postgres-operator + namespace: cozy-postgres-operator + dependsOn: [cert-manager,victoria-metrics-operator] + + - name: objectstorage-controller + releaseName: objectstorage-controller + path: system/objectstorage-controller + namespace: cozy-objectstorage-controller + dependsOn: [] + + - name: telepresence + releaseName: traffic-manager + path: system/telepresence + namespace: cozy-telepresence + disabled: true + dependsOn: [] + + - name: external-dns + releaseName: external-dns + path: system/external-dns + namespace: cozy-external-dns + disabled: true + dependsOn: [] + + - name: external-secrets-operator + releaseName: external-secrets-operator + path: system/external-secrets-operator + namespace: cozy-external-secrets-operator + disabled: true + dependsOn: [] + + - name: dashboard + releaseName: dashboard + path: system/dashboard + namespace: cozy-dashboard + dependsOn: + - cilium + - kubeovn + - cozystack-api + - cozystack-controller + {{- if $oidcEnabled }} + - keycloak-configure + {{- end }} + values: + cozystack: + publishing: + host: {{ $host }} + ingressClassName: {{ .Values.publishing.ingressName | default "tenant-root" }} + exposedServices: {{ .Values.publishing.exposedServices | toJson }} + certificates: + issuerType: {{ .Values.publishing.certificates.issuerType | default "http01" }} + authentication: + oidc: + enabled: {{ $oidcEnabled }} + dashboard: + extraRedirectUris: {{ .Values.authentication.oidc.dashboard.extraRedirectUris | toJson }} + {{- if hasKey .Values.branding "dashboard" }} + branding: + dashboard: + {{- toYaml .Values.branding.dashboard | nindent 14 }} + {{- end }} + + {{- if $oidcEnabled }} + - name: keycloak + releaseName: keycloak + path: system/keycloak + namespace: cozy-keycloak + dependsOn: [postgres-operator] + libraries: [cozy-lib] + values: + cozystack: + publishing: + host: {{ $host }} + ingressClassName: {{ .Values.publishing.ingressName | default "tenant-root" }} + certificates: + issuerType: {{ .Values.publishing.certificates.issuerType | default "http01" }} + networking: + clusterDomain: {{ $clusterDomain }} + scheduling: + topologySpreadConstraints: {{ .Values.scheduling.topologySpreadConstraints | default list | toJson }} + + - name: keycloak-operator + releaseName: keycloak-operator + path: system/keycloak-operator + namespace: cozy-keycloak + dependsOn: [keycloak] + + - name: keycloak-configure + releaseName: keycloak-configure + path: system/keycloak-configure + namespace: cozy-keycloak + dependsOn: [keycloak-operator] + values: + cozystack: + publishing: + host: {{ $host }} + authentication: + oidc: + dashboard: + extraRedirectUris: {{ .Values.authentication.oidc.dashboard.extraRedirectUris | toJson }} + {{- if hasKey .Values.branding "keycloak" }} + branding: + {{- toYaml .Values.branding.keycloak | nindent 12 }} + {{- end }} + {{- end }} + + - name: goldpinger + releaseName: goldpinger + path: system/goldpinger + namespace: cozy-goldpinger + privileged: true + dependsOn: [monitoring-agents] + + - name: vertical-pod-autoscaler + releaseName: vertical-pod-autoscaler + path: system/vertical-pod-autoscaler + namespace: cozy-vertical-pod-autoscaler + privileged: true + dependsOn: [monitoring-agents] + values: + vertical-pod-autoscaler: + recommender: + extraArgs: + prometheus-address: http://vmselect-shortterm.tenant-root.svc.{{ $clusterDomain }}:8481/select/0/prometheus/ + + - name: vertical-pod-autoscaler-crds + releaseName: vertical-pod-autoscaler-crds + path: system/vertical-pod-autoscaler-crds + namespace: cozy-vertical-pod-autoscaler + privileged: true + dependsOn: [] + + - name: velero + releaseName: velero + path: system/velero + namespace: cozy-velero + privileged: true + disabled: true + dependsOn: [monitoring-agents] + + - name: hetzner-robotlb + releaseName: robotlb + disabled: true + path: system/hetzner-robotlb + namespace: cozy-hetzner-robotlb diff --git a/packages/core/platform/bundles/system/bundle-minimal.yaml b/packages/core/platform/bundles/system/bundle-minimal.yaml new file mode 100644 index 00000000..5383e4da --- /dev/null +++ b/packages/core/platform/bundles/system/bundle-minimal.yaml @@ -0,0 +1,82 @@ +{{- $host := .Values.publishing.host }} +{{- if not $host }} +{{- fail "ERROR: publishing.host is required" }} +{{- end }} +{{- $oidcEnabled := .Values.authentication.oidc.enabled | default false }} + +apiVersion: cozystack.io/v1alpha1 +kind: Bundle +metadata: + name: cozystack-minimal +spec: + deletionPolicy: Orphan + + dependencyTargets: + - name: network + packages: [] + + sourceRef: + kind: {{ .Values.sourceRef.kind }} + name: {{ .Values.sourceRef.name }} + namespace: {{ .Values.sourceRef.namespace }} + + libraries: + - name: cozy-lib + path: library/cozy-lib + + artifacts: + - name: tenant + path: apps/tenant + libraries: [cozy-lib] + + packages: + - name: cozystack-api + releaseName: cozystack-api + path: system/cozystack-api + namespace: cozy-system + dependsOn: [cozystack-controller] + values: + cozystackAPI: + localK8sAPIEndpoint: + enabled: false + + - name: cozystack-controller + releaseName: cozystack-controller + path: system/cozystack-controller + namespace: cozy-system + dependsOn: [] + + - name: lineage-controller-webhook + releaseName: lineage-controller-webhook + path: system/lineage-controller-webhook + namespace: cozy-system + dependsOn: [cozystack-controller] + + - name: dashboard + releaseName: dashboard + path: system/dashboard + namespace: cozy-dashboard + dependsOn: + - cozystack-api + - cozystack-controller + values: + cozystack: + publishing: + host: {{ $host }} + ingressClassName: {{ .Values.publishing.ingressName | default "tenant-root" }} + exposedServices: {{ .Values.publishing.exposedServices | toJson }} + certificates: + issuerType: {{ .Values.publishing.certificates.issuerType | default "http01" }} + authentication: + oidc: + enabled: {{ $oidcEnabled }} + dashboard: + extraRedirectUris: {{ .Values.authentication.oidc.dashboard.extraRedirectUris | toJson }} + branding: + dashboard: + tenantText: {{ .Values.branding.dashboard.tenantText | default "v0.38.0-alpha.1" | quote }} + footerText: {{ .Values.branding.dashboard.footerText | default "Cozystack" | quote }} + titleText: {{ .Values.branding.dashboard.titleText | default "Cozystack Dashboard" | quote }} + logoText: {{ .Values.branding.dashboard.logoText | default "" | quote }} + logoSvg: {{ .Values.branding.dashboard.logoSvg | default "" | quote }} + iconSvg: {{ .Values.branding.dashboard.iconSvg | default "" | quote }} diff --git a/packages/core/platform/images/migrations/Dockerfile b/packages/core/platform/images/migrations/Dockerfile new file mode 100644 index 00000000..9acda394 --- /dev/null +++ b/packages/core/platform/images/migrations/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.22 + +RUN wget -O- https://github.com/cozystack/cozypkg/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.4.0 + +RUN apk add --no-cache kubectl helm coreutils git jq ca-certificates bash curl + +COPY migrations /migrations +COPY run-migrations.sh /usr/bin/run-migrations.sh + +WORKDIR /migrations + +ENTRYPOINT ["/usr/bin/run-migrations.sh"] + diff --git a/scripts/migrations/1 b/packages/core/platform/images/migrations/migrations/1 similarity index 100% rename from scripts/migrations/1 rename to packages/core/platform/images/migrations/migrations/1 diff --git a/scripts/migrations/10 b/packages/core/platform/images/migrations/migrations/10 similarity index 100% rename from scripts/migrations/10 rename to packages/core/platform/images/migrations/migrations/10 diff --git a/scripts/migrations/11 b/packages/core/platform/images/migrations/migrations/11 similarity index 100% rename from scripts/migrations/11 rename to packages/core/platform/images/migrations/migrations/11 diff --git a/scripts/migrations/12 b/packages/core/platform/images/migrations/migrations/12 similarity index 100% rename from scripts/migrations/12 rename to packages/core/platform/images/migrations/migrations/12 diff --git a/scripts/migrations/13 b/packages/core/platform/images/migrations/migrations/13 similarity index 100% rename from scripts/migrations/13 rename to packages/core/platform/images/migrations/migrations/13 diff --git a/scripts/migrations/14 b/packages/core/platform/images/migrations/migrations/14 similarity index 100% rename from scripts/migrations/14 rename to packages/core/platform/images/migrations/migrations/14 diff --git a/scripts/migrations/15 b/packages/core/platform/images/migrations/migrations/15 similarity index 100% rename from scripts/migrations/15 rename to packages/core/platform/images/migrations/migrations/15 diff --git a/scripts/migrations/16 b/packages/core/platform/images/migrations/migrations/16 similarity index 100% rename from scripts/migrations/16 rename to packages/core/platform/images/migrations/migrations/16 diff --git a/scripts/migrations/17 b/packages/core/platform/images/migrations/migrations/17 similarity index 100% rename from scripts/migrations/17 rename to packages/core/platform/images/migrations/migrations/17 diff --git a/scripts/migrations/18 b/packages/core/platform/images/migrations/migrations/18 similarity index 100% rename from scripts/migrations/18 rename to packages/core/platform/images/migrations/migrations/18 diff --git a/scripts/migrations/19 b/packages/core/platform/images/migrations/migrations/19 similarity index 100% rename from scripts/migrations/19 rename to packages/core/platform/images/migrations/migrations/19 diff --git a/scripts/migrations/2 b/packages/core/platform/images/migrations/migrations/2 similarity index 76% rename from scripts/migrations/2 rename to packages/core/platform/images/migrations/migrations/2 index 0c082452..a3bfd118 100755 --- a/scripts/migrations/2 +++ b/packages/core/platform/images/migrations/migrations/2 @@ -1,7 +1,9 @@ #!/bin/sh # Migration 2 --> 3 -kubectl apply -f packages/system/mariadb-operator/charts/mariadb-operator/crds/crds.yaml --server-side --force-conflicts +if [ -d packages/system ]; then + kubectl apply -f packages/system/mariadb-operator/charts/mariadb-operator/crds/crds.yaml --server-side --force-conflicts +fi # Fix mariadb-operator crds mariadb_crds=$(kubectl get crd -o name | grep '\.k8s\.mariadb\.com$') diff --git a/scripts/migrations/20 b/packages/core/platform/images/migrations/migrations/20 similarity index 63% rename from scripts/migrations/20 rename to packages/core/platform/images/migrations/migrations/20 index 2b2e1530..0122c054 100755 --- a/scripts/migrations/20 +++ b/packages/core/platform/images/migrations/migrations/20 @@ -22,25 +22,14 @@ done kubectl delete helmrelease -n cozy-dashboard dashboard --ignore-not-found sleep 5 -cozypkg -n cozy-system -C packages/system/cozystack-resource-definition-crd apply cozystack-resource-definition-crd --plain -cozypkg -n cozy-system -C packages/system/cozystack-resource-definitions apply cozystack-resource-definitions --plain -cozypkg -n cozy-system -C packages/system/cozystack-api apply cozystack-api --plain -if kubectl get ds cozystack-api -n cozy-system >/dev/null 2>&1; then - echo "Waiting for cozystack-api daemonset" - kubectl rollout status ds/cozystack-api -n cozy-system --timeout=5m || exit 1 -else - echo "Waiting for cozystack-api deployment" - kubectl rollout status deploy/cozystack-api -n cozy-system --timeout=5m || exit 1 +if [ -d "packages/system" ]; then + cozypkg -n cozy-system -C packages/system/cozystack-resource-definition-crd apply cozystack-resource-definition-crd --plain + cozypkg -n cozy-system -C packages/system/cozystack-resource-definitions apply cozystack-resource-definitions --plain + cozypkg -n cozy-system -C packages/system/cozystack-api apply cozystack-api --plain + cozypkg -n cozy-system -C packages/system/cozystack-controller/ apply cozystack-api --plain --take-ownership + cozypkg -n cozy-system -C packages/system/lineage-controller-webhook/ apply cozystack-api --plain --take-ownership fi -helm upgrade --install -n cozy-system cozystack-controller ./packages/system/cozystack-controller/ --take-ownership -echo "Waiting for cozystack-controller" -kubectl rollout status deploy/cozystack-controller -n cozy-system --timeout=5m || exit 1 - -helm upgrade --install -n cozy-system lineage-controller-webhook ./packages/system/lineage-controller-webhook/ --take-ownership -echo "Waiting for lineage-webhook" -kubectl rollout status ds/lineage-controller-webhook -n cozy-system --timeout=5m || exit 1 - sleep 5 echo "Running lineage-webhook test" kubectl delete ns cozy-lineage-webhook-test --ignore-not-found && kubectl create ns cozy-lineage-webhook-test diff --git a/packages/core/platform/images/migrations/migrations/21 b/packages/core/platform/images/migrations/migrations/21 new file mode 100755 index 00000000..c065d23c --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/21 @@ -0,0 +1,21 @@ +#!/bin/sh +# Migration 21 --> 22 + +set -euo pipefail + +# Delete old cozystack deployment +kubectl -n cozy-system delete deployment cozystack --ignore-not-found +while [ $( kubectl -n cozy-system get po -l app=cozystack --no-headers 2>/dev/null | wc -l ) != 0 ] ; +do + echo "Waiting for old cozystack deployment pods to be deleted"; + sleep 1 +done + +kubectl delete hr -n cozy-system cozystack-resource-definitions --ignore-not-found +kubectl delete hr -n cozy-system cozystack-resource-definition-crd --ignore-not-found +kubectl delete crd cozystackresourcedefinitions.cozycloud.io --ignore-not-found +kubectl delete hr -n cozy-fluxcd fluxcd --ignore-not-found + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=22 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/22 b/packages/core/platform/images/migrations/migrations/22 new file mode 100755 index 00000000..97beb6f0 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/22 @@ -0,0 +1,161 @@ +#!/bin/sh +# Migration 22 --> 23 + +set -euo pipefail + +echo "Migrating HelmReleases: adding application labels for tenant-* namespaces" + +# Function to determine application type from HelmRelease name +determine_app_type() { + local name="$1" + local app_kind="" + local app_name="" + + # Try to match by prefix (longest match first) + case "$name" in + virtual-machine-*) + app_kind="VirtualMachine" + app_name="${name#virtual-machine-}" + ;; + vm-instance-*) + app_kind="VMInstance" + app_name="${name#vm-instance-}" + ;; + vm-disk-*) + app_kind="VMDisk" + app_name="${name#vm-disk-}" + ;; + virtualprivatecloud-*) + app_kind="VirtualPrivateCloud" + app_name="${name#virtualprivatecloud-}" + ;; + http-cache-*) + app_kind="HTTPCache" + app_name="${name#http-cache-}" + ;; + tcp-balancer-*) + app_kind="TCPBalancer" + app_name="${name#tcp-balancer-}" + ;; + clickhouse-*) + app_kind="ClickHouse" + app_name="${name#clickhouse-}" + ;; + foundationdb-*) + app_kind="FoundationDB" + app_name="${name#foundationdb-}" + ;; + ferretdb-*) + app_kind="FerretDB" + app_name="${name#ferretdb-}" + ;; + rabbitmq-*) + app_kind="RabbitMQ" + app_name="${name#rabbitmq-}" + ;; + kubernetes-*) + app_kind="Kubernetes" + app_name="${name#kubernetes-}" + ;; + bucket-*) + app_kind="Bucket" + app_name="${name#bucket-}" + ;; + kafka-*) + app_kind="Kafka" + app_name="${name#kafka-}" + ;; + mysql-*) + app_kind="MySQL" + app_name="${name#mysql-}" + ;; + nats-*) + app_kind="NATS" + app_name="${name#nats-}" + ;; + postgres-*) + app_kind="PostgreSQL" + app_name="${name#postgres-}" + ;; + redis-*) + app_kind="Redis" + app_name="${name#redis-}" + ;; + tenant-*) + app_kind="Tenant" + app_name="${name#tenant-}" + ;; + vpn-*) + app_kind="VPN" + app_name="${name#vpn-}" + ;; + bootbox) + app_kind="BootBox" + app_name="bootbox" + ;; + etcd) + app_kind="Etcd" + app_name="etcd" + ;; + info) + app_kind="Info" + app_name="info" + ;; + ingress|ingress-*) + app_kind="Ingress" + if [ "$name" = "ingress" ]; then + app_name="ingress" + else + app_name="${name#ingress-}" + fi + ;; + monitoring) + app_kind="Monitoring" + app_name="monitoring" + ;; + seaweedfs) + app_kind="SeaweedFS" + app_name="seaweedfs" + ;; + *) + # Unknown type + return 1 + ;; + esac + + echo "$app_kind|$app_name" + return 0 +} + +# Process all HelmReleases in tenant-* namespaces with cozystack.io/ui=true label +kubectl get helmreleases --all-namespaces -l cozystack.io/ui=true -o json | \ + jq -r '.items[] | select(.metadata.namespace | startswith("tenant-")) | "\(.metadata.namespace)|\(.metadata.name)"' | \ + while IFS='|' read -r namespace name; do + echo "Processing HelmRelease $namespace/$name" + + # Determine application type + app_type=$(determine_app_type "$name") + if [ $? -ne 0 ] || [ -z "$app_type" ]; then + echo "Warning: Could not determine application type for $namespace/$name, skipping" + continue + fi + + app_kind=$(echo "$app_type" | cut -d'|' -f1) + app_name=$(echo "$app_type" | cut -d'|' -f2) + app_group="apps.cozystack.io" + + # Build labels string + labels="apps.cozystack.io/application.kind=$app_kind" + labels="$labels apps.cozystack.io/application.group=$app_group" + labels="$labels apps.cozystack.io/application.name=$app_name" + + # Apply labels using kubectl label --overwrite + kubectl label helmrelease -n "$namespace" "$name" --overwrite $labels + echo "Added application labels to $namespace/$name: $labels" + done + +echo "Migration completed" + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=23 --dry-run=client -o yaml | kubectl apply -f- diff --git a/scripts/migrations/3 b/packages/core/platform/images/migrations/migrations/3 similarity index 100% rename from scripts/migrations/3 rename to packages/core/platform/images/migrations/migrations/3 diff --git a/scripts/migrations/4 b/packages/core/platform/images/migrations/migrations/4 similarity index 100% rename from scripts/migrations/4 rename to packages/core/platform/images/migrations/migrations/4 diff --git a/scripts/migrations/5 b/packages/core/platform/images/migrations/migrations/5 similarity index 100% rename from scripts/migrations/5 rename to packages/core/platform/images/migrations/migrations/5 diff --git a/scripts/migrations/6 b/packages/core/platform/images/migrations/migrations/6 similarity index 100% rename from scripts/migrations/6 rename to packages/core/platform/images/migrations/migrations/6 diff --git a/scripts/migrations/7 b/packages/core/platform/images/migrations/migrations/7 similarity index 100% rename from scripts/migrations/7 rename to packages/core/platform/images/migrations/migrations/7 diff --git a/scripts/migrations/8 b/packages/core/platform/images/migrations/migrations/8 similarity index 100% rename from scripts/migrations/8 rename to packages/core/platform/images/migrations/migrations/8 diff --git a/scripts/migrations/9 b/packages/core/platform/images/migrations/migrations/9 similarity index 100% rename from scripts/migrations/9 rename to packages/core/platform/images/migrations/migrations/9 diff --git a/packages/core/platform/images/migrations/run-migrations.sh b/packages/core/platform/images/migrations/run-migrations.sh new file mode 100755 index 00000000..8e4cc1c6 --- /dev/null +++ b/packages/core/platform/images/migrations/run-migrations.sh @@ -0,0 +1,42 @@ +#!/bin/sh +set -euo pipefail + +NAMESPACE="${NAMESPACE:-cozy-system}" +CURRENT_VERSION="${CURRENT_VERSION:-0}" +TARGET_VERSION="${TARGET_VERSION:-0}" + +echo "Starting migrations from version $CURRENT_VERSION to $TARGET_VERSION" + +# Check if ConfigMap exists +if ! kubectl get configmap -n "$NAMESPACE" cozystack-version >/dev/null 2>&1; then + echo "ConfigMap cozystack-version does not exist, creating it with version $TARGET_VERSION" + kubectl create configmap -n "$NAMESPACE" cozystack-version \ + --from-literal=version="$TARGET_VERSION" \ + --dry-run=client -o yaml | kubectl apply -f- + echo "ConfigMap created with version $TARGET_VERSION" + exit 0 +fi + +# If current version is already at target, nothing to do +if [ "$CURRENT_VERSION" -ge "$TARGET_VERSION" ]; then + echo "Current version $CURRENT_VERSION is already at or above target version $TARGET_VERSION" + exit 0 +fi + +# Run migrations sequentially from current version to target version +for i in $(seq $((CURRENT_VERSION + 1)) $TARGET_VERSION); do + if [ -f "/migrations/$i" ]; then + echo "Running migration $i" + chmod +x /migrations/$i + /migrations/$i || { + echo "Migration $i failed" + exit 1 + } + echo "Migration $i completed successfully" + else + echo "Migration $i not found, skipping" + fi +done + +echo "All migrations completed successfully" + diff --git a/packages/core/platform/templates/_helpers.tpl b/packages/core/platform/templates/_helpers.tpl index 73a4574c..38c84168 100644 --- a/packages/core/platform/templates/_helpers.tpl +++ b/packages/core/platform/templates/_helpers.tpl @@ -17,62 +17,147 @@ Get IP-addresses of master nodes {{ join "," $ips }} {{- end -}} -{{- define "cozystack.defaultDashboardValues" -}} -kubeapps: -{{- if .Capabilities.APIVersions.Has "source.toolkit.fluxcd.io/v1" }} -{{- with (lookup "source.toolkit.fluxcd.io/v1" "HelmRepository" "cozy-public" "").items }} - redis: - master: - podAnnotations: - {{- range $index, $repo := . }} - {{- with (($repo.status).artifact).revision }} - repository.cozystack.io/{{ $repo.metadata.name }}: {{ quote . }} - {{- end }} - {{- end }} -{{- end }} -{{- end }} - frontend: - resourcesPreset: "none" - dashboard: - resourcesPreset: "none" - {{- $cozystackBranding:= lookup "v1" "ConfigMap" "cozy-system" "cozystack-branding" }} - {{- $branding := dig "data" "branding" "" $cozystackBranding }} - {{- if $branding }} - customLocale: - "Kubeapps": {{ $branding }} +{{/* +Apply component overrides to a package and return modified package as YAML +Usage: {{ include "cozystack.apply-package-overrides" (list . $package) }} +*/}} +{{- define "cozystack.apply-package-overrides" -}} +{{- $ := index . 0 }} +{{- $package := index . 1 }} +{{- $packageCopy := deepCopy $package }} +{{- $componentName := $packageCopy.name }} +{{- $component := index $.Values.components $componentName }} +{{- if $component }} + {{- /* Apply enabled/disabled */}} + {{- if hasKey $component "enabled" }} + {{- if not $component.enabled }} + {{- $_ := set $packageCopy "disabled" true }} + {{- else if hasKey $packageCopy "disabled" }} + {{- $_ := unset $packageCopy "disabled" }} {{- end }} - customStyle: | - {{- $logoImage := dig "data" "logo" "" $cozystackBranding }} - {{- if $logoImage }} - .kubeapps-logo { - background-image: {{ $logoImage }} - } - {{- end }} - #serviceaccount-selector { - display: none; - } - .login-moreinfo { - display: none; - } - a[href="#/docs"] { - display: none; - } - .login-group .clr-form-control .clr-control-label { - display: none; - } - .appview-separator div.appview-first-row div.center { - display: none; - } - .appview-separator div.appview-first-row section[aria-labelledby="app-secrets"] { - display: none; - } - .appview-first-row section[aria-labelledby="access-urls-title"] { - width: 100%; - } - .header-version { - display: none; - } - .label.label-info-secondary { - display: none; - } + {{- end }} + {{- /* Apply values override */}} + {{- if $component.values }} + {{- if hasKey $packageCopy "values" }} + {{- $_ := set $packageCopy "values" (deepCopy $packageCopy.values | mergeOverwrite (deepCopy $component.values)) }} + {{- else }} + {{- $_ := set $packageCopy "values" (deepCopy $component.values) }} + {{- end }} + {{- end }} {{- end }} +{{- $packageCopy | toYaml }} +{{- end -}} + +{{/* +Render a template file with tpl and trim, applying component overrides +Usage: {{ include "cozystack.render-file" (list . "bundles/system/bundle-full.yaml") }} +*/}} +{{- define "cozystack.render-file" -}} +{{- $ := index . 0 }} +{{- $filePath := index . 1 }} +{{- $rendered := trim (tpl ($.Files.Get $filePath) $) }} +{{- $bundle := $rendered | fromYaml }} +{{- if and $bundle $bundle.spec $bundle.spec.packages }} +{{- $modifiedPackages := list }} +{{- range $package := $bundle.spec.packages }} + {{- $modifiedPackageYaml := include "cozystack.apply-package-overrides" (list $ $package) }} + {{- $modifiedPackage := $modifiedPackageYaml | fromYaml }} + {{- $modifiedPackages = append $modifiedPackages $modifiedPackage }} +{{- end }} +{{- $bundleCopy := deepCopy $bundle }} +{{- $_ := set $bundleCopy.spec "packages" $modifiedPackages }} +--- +{{ $bundleCopy | toYaml }} +{{- else }} +--- +{{ $rendered }} +{{- end }} +{{- end -}} + +{{/* +Render all files matching a glob pattern with template processing +Usage: {{ include "cozystack.render-glob" (list . "bundles/system/applicationdefinitions/*.yaml") }} +*/}} +{{- define "cozystack.render-glob" -}} +{{- $ := index . 0 }} +{{- $pattern := index . 1 }} +{{- range $path, $_ := $.Files.Glob $pattern }} +--- +{{ trim (tpl ($.Files.Get $path) $) }} +{{- end }} +{{- end -}} + +{{/* +Check if a component is enabled +Usage: {{ include "cozystack.component-enabled" (list . "metallb") }} +Returns: true if component is enabled, false otherwise +*/}} +{{- define "cozystack.component-enabled" -}} +{{- $ := index . 0 }} +{{- $componentName := index . 1 }} +{{- $component := index $.Values.components $componentName }} +{{- if $component }} + {{- if hasKey $component "enabled" }} + {{- $component.enabled }} + {{- else }} + {{- true }} + {{- end }} +{{- else }} + {{- true }} +{{- end }} +{{- end -}} + +{{/* +Get component values override +Usage: {{ include "cozystack.component-values" (list . "cilium") }} +Returns: YAML string with component values or empty string +*/}} +{{- define "cozystack.component-values" -}} +{{- $ := index . 0 }} +{{- $componentName := index . 1 }} +{{- $component := index $.Values.components $componentName }} +{{- if $component }} + {{- if $component.values }} + {{- toYaml $component.values | nindent 2 }} + {{- end }} +{{- end }} +{{- end -}} + +{{/* +Merge component values into existing values +Usage: {{ include "cozystack.merge-component-values" (list . "cilium" (dict "key" "value")) }} +Returns: Merged values dictionary +*/}} +{{- define "cozystack.merge-component-values" -}} +{{- $ := index . 0 }} +{{- $componentName := index . 1 }} +{{- $defaultValues := index . 2 }} +{{- $component := index $.Values.components $componentName }} +{{- if $component }} + {{- if $component.values }} + {{- mergeOverwrite $defaultValues $component.values }} + {{- else }} + {{- $defaultValues }} + {{- end }} +{{- else }} + {{- $defaultValues }} +{{- end }} +{{- end -}} + +{{/* +Build cozystack values structure from root values +Usage: {{ include "cozystack.build-values" . | nindent 8 }} +Returns: YAML string with cozystack values structure +*/}} +{{- define "cozystack.build-values" -}} +{{- $cozystack := dict }} +{{- if .Values.networking }}{{ $_ := set $cozystack "networking" .Values.networking }}{{ end }} +{{- if .Values.publishing }}{{ $_ := set $cozystack "publishing" .Values.publishing }}{{ end }} +{{- if .Values.scheduling }}{{ $_ := set $cozystack "scheduling" .Values.scheduling }}{{ end }} +{{- if .Values.authentication }}{{ $_ := set $cozystack "authentication" .Values.authentication }}{{ end }} +{{- if .Values.branding }}{{ $_ := set $cozystack "branding" .Values.branding }}{{ end }} +{{- if .Values.registries }}{{ $_ := set $cozystack "registries" .Values.registries }}{{ end }} +{{- if .Values.resources }}{{ $_ := set $cozystack "resources" .Values.resources }}{{ end }} +{{- if .Values.components }}{{ $_ := set $cozystack "components" .Values.components }}{{ end }} +{{- $cozystack | toYaml | trim }} +{{- end -}} diff --git a/packages/core/platform/templates/applicationdefinitions.yaml b/packages/core/platform/templates/applicationdefinitions.yaml new file mode 100644 index 00000000..c35dbd82 --- /dev/null +++ b/packages/core/platform/templates/applicationdefinitions.yaml @@ -0,0 +1,15 @@ +{{- $systemBundleType := .Values.bundles.system.type | default "full" }} + +{{- if or (ne $systemBundleType "full") (ne $systemBundleType "hosted") }} +{{ include "cozystack.render-glob" (list . "bundles/system/applicationdefinitions/*.yaml") }} +{{- end }} + +{{- if .Values.bundles.iaas.enabled }} +{{ include "cozystack.render-glob" (list . "bundles/iaas/applicationdefinitions/*.yaml") }} +{{- end }} +{{- if .Values.bundles.paas.enabled }} +{{ include "cozystack.render-glob" (list . "bundles/paas/applicationdefinitions/*.yaml") }} +{{- end }} +{{- if .Values.bundles.naas.enabled }} +{{ include "cozystack.render-glob" (list . "bundles/naas/applicationdefinitions/*.yaml") }} +{{- end }} diff --git a/packages/core/platform/templates/apps.yaml b/packages/core/platform/templates/apps.yaml deleted file mode 100644 index c7653e03..00000000 --- a/packages/core/platform/templates/apps.yaml +++ /dev/null @@ -1,64 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $bundleName := index $cozyConfig.data "bundle-name" }} -{{- $bundle := tpl (.Files.Get (printf "bundles/%s.yaml" $bundleName)) . | fromYaml }} -{{- $host := "example.org" }} -{{- $host := "example.org" }} -{{- if $cozyConfig.data }} - {{- if hasKey $cozyConfig.data "root-host" }} - {{- $host = index $cozyConfig.data "root-host" }} - {{- end }} -{{- end }} -{{- $tenantRoot := dict }} -{{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} -{{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} -{{- end }} -{{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} -{{- $host = $tenantRoot.spec.values.host }} -{{- else }} -{{- end }} ---- -apiVersion: v1 -kind: Namespace -metadata: - annotations: - helm.sh/resource-policy: keep - namespace.cozystack.io/etcd: tenant-root - namespace.cozystack.io/monitoring: tenant-root - namespace.cozystack.io/ingress: tenant-root - namespace.cozystack.io/seaweedfs: tenant-root - namespace.cozystack.io/host: "{{ $host }}" - name: tenant-root ---- -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: tenant-root - namespace: tenant-root - labels: - cozystack.io/ui: "true" -spec: - interval: 0s - releaseName: tenant-root - install: - remediation: - retries: -1 - upgrade: - remediation: - retries: -1 - chart: - spec: - chart: tenant - version: '>= 0.0.0-0' - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public - values: - host: "{{ $host }}" - dependsOn: - {{- range $x := $bundle.releases }} - {{- if has $x.name (list "cilium" "kubeovn") }} - - name: {{ $x.name }} - namespace: {{ $x.namespace }} - {{- end }} - {{- end }} diff --git a/packages/core/platform/templates/bundles.yaml b/packages/core/platform/templates/bundles.yaml new file mode 100644 index 00000000..e05907e7 --- /dev/null +++ b/packages/core/platform/templates/bundles.yaml @@ -0,0 +1,40 @@ +{{- $systemBundleType := .Values.bundles.system.type | default "full" }} + +{{- /* ====================================================================== */}} +{{- /* System Bundle Deployment */}} +{{- /* ====================================================================== */}} + +{{- if eq $systemBundleType "minimal" }} +{{ include "cozystack.render-file" (list . "bundles/system/bundle-minimal.yaml") }} +{{- else if eq $systemBundleType "full" }} +{{ include "cozystack.render-file" (list . "bundles/system/bundle-full.yaml") }} +{{- else if eq $systemBundleType "hosted" }} +{{ include "cozystack.render-file" (list . "bundles/system/bundle-hosted.yaml") }} +{{- else }} +{{- fail (printf "ERROR: unknown system bundle type '%s'. Supported values: 'minimal', 'full', 'hosted'" $systemBundleType) }} +{{- end }} + +{{- /* IaaS bundle: only available with system type "full" */}} +{{- if .Values.bundles.iaas.enabled }} +{{- if ne $systemBundleType "full" }} +{{- fail "ERROR: iaas bundle can only be used with system bundle type 'full'" }} +{{- end }} +{{ include "cozystack.render-file" (list . "bundles/iaas/bundle.yaml") }} +{{- end }} + +{{- /* PaaS bundle: only available with system type "full" or "hosted" */}} +{{- if .Values.bundles.paas.enabled }} +{{- if and (ne $systemBundleType "full") (ne $systemBundleType "hosted") }} +{{- fail "ERROR: paas bundle can only be used with system bundle type 'full' or 'hosted'" }} +{{- end }} +{{ include "cozystack.render-file" (list . "bundles/paas/bundle.yaml") }} +{{- end }} + +{{- /* NaaS bundle: only available with system type "full" or "hosted" */}} +{{- if .Values.bundles.naas.enabled }} +{{- if and (ne $systemBundleType "full") (ne $systemBundleType "hosted") }} +{{- fail "ERROR: naas bundle can only be used with system bundle type 'full' or 'hosted'" }} +{{- end }} +{{ include "cozystack.render-file" (list . "bundles/naas/bundle.yaml") }} +{{- end }} + diff --git a/packages/core/platform/templates/containerd-registry-secret.yaml b/packages/core/platform/templates/containerd-registry-secret.yaml new file mode 100644 index 00000000..6e9fb640 --- /dev/null +++ b/packages/core/platform/templates/containerd-registry-secret.yaml @@ -0,0 +1,35 @@ +{{- if .Values.registries.mirrors }} +apiVersion: v1 +kind: Secret +metadata: + name: patch-containerd + namespace: cozy-system +type: Opaque +stringData: +{{- range $registry, $mirror := .Values.registries.mirrors }} +{{- if $mirror.endpoints }} + {{ $registry }}.toml: | + server = "https://{{ $registry }}" +{{- range $endpoint := $mirror.endpoints }} + [host."{{ $endpoint }}"] + capabilities = ["pull", "resolve"] +{{- $endpointConfig := index $.Values.registries.config $endpoint }} +{{- if $endpointConfig }} +{{- if $endpointConfig.tls }} +{{- if $endpointConfig.tls.insecureSkipVerify }} + skip_verify = true +{{- end }} +{{- end }} +{{- if $endpointConfig.auth }} + [host."{{ $endpoint }}".auth] + username = "{{ $endpointConfig.auth.username }}" + password = "{{ $endpointConfig.auth.password }}" +{{- end }} +{{- else }} + skip_verify = true +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} + diff --git a/packages/core/platform/templates/helmreleases.yaml b/packages/core/platform/templates/helmreleases.yaml deleted file mode 100644 index 6ed61ed8..00000000 --- a/packages/core/platform/templates/helmreleases.yaml +++ /dev/null @@ -1,97 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $bundleName := index $cozyConfig.data "bundle-name" }} -{{- $bundle := tpl (.Files.Get (printf "bundles/%s.yaml" $bundleName)) . | fromYaml }} -{{- $dependencyNamespaces := dict }} -{{- $disabledComponents := splitList "," ((index $cozyConfig.data "bundle-disable") | default "") }} -{{- $enabledComponents := splitList "," ((index $cozyConfig.data "bundle-enable") | default "") }} -{{- $oidcEnabled := (index (default dict $cozyConfig.data) "oidc-enabled") | default "false" | eq "true" }} - -{{/* collect dependency namespaces from releases */}} -{{- range $x := $bundle.releases }} -{{- $_ := set $dependencyNamespaces $x.name $x.namespace }} -{{- end }} - -{{- range $x := $bundle.releases }} - -{{- $shouldInstall := true }} -{{- $shouldDelete := false }} -{{- $notEnabledOptionalComponent := and ($x.optional) (not (has $x.name $enabledComponents)) }} -{{- $disabledComponent := has $x.name $disabledComponents }} -{{- $isKeycloakComponent := or (eq $x.name "keycloak") (eq $x.name "keycloak-operator") (eq $x.name "keycloak-configure") }} - -{{- if and $isKeycloakComponent (not $oidcEnabled) }} -{{- $shouldInstall = false }} -{{- if $.Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} -{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" $x.namespace $x.name }} -{{- $shouldDelete = true }} -{{- end }} -{{- end }} -{{- else if or $disabledComponent $notEnabledOptionalComponent }} -{{- $shouldInstall = false }} -{{- if $.Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} -{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" $x.namespace $x.name }} -{{- $shouldDelete = true }} -{{- end }} -{{- end }} -{{- end }} - -{{- if or $shouldInstall $shouldDelete }} ---- -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: {{ $x.name }} - namespace: {{ $x.namespace }} - labels: - cozystack.io/repository: system - cozystack.io/system-app: "true" - {{- if $shouldDelete }} - cozystack.io/marked-for-deletion: "true" - {{- end }} -spec: - interval: 5m - releaseName: {{ $x.releaseName | default $x.name }} - install: - crds: CreateReplace - remediation: - retries: -1 - upgrade: - crds: CreateReplace - remediation: - retries: -1 - chart: - spec: - chart: {{ $x.chart }} - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' - {{- with $x.valuesFiles }} - valuesFiles: - {{- toYaml $x.valuesFiles | nindent 6 }} - {{- end }} - {{- $values := dict }} - {{- with $x.values }} - {{- $values = merge . $values }} - {{- end }} - {{- with index $cozyConfig.data (printf "values-%s" $x.name) }} - {{- $values = mergeOverwrite $values (fromYaml .) }} - {{- end }} - {{- with $values }} - values: - {{- toYaml . | nindent 4}} - {{- end }} - - {{- with $x.dependsOn }} - dependsOn: - {{- range $dep := . }} - {{- if not (has $dep $disabledComponents) }} - - name: {{ $dep }} - namespace: {{ index $dependencyNamespaces $dep }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} -{{- end }} diff --git a/packages/core/platform/templates/helmrepos.yaml b/packages/core/platform/templates/helmrepos.yaml deleted file mode 100644 index 69f77534..00000000 --- a/packages/core/platform/templates/helmrepos.yaml +++ /dev/null @@ -1,34 +0,0 @@ ---- -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: cozystack-system - namespace: cozy-system - labels: - cozystack.io/repository: system -spec: - interval: 5m0s - url: http://cozystack.cozy-system.svc/repos/system ---- -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: cozystack-apps - namespace: cozy-public - labels: - cozystack.io/ui: "true" - cozystack.io/repository: apps -spec: - interval: 5m0s - url: http://cozystack.cozy-system.svc/repos/apps ---- -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: cozystack-extra - namespace: cozy-public - labels: - cozystack.io/repository: extra -spec: - interval: 5m0s - url: http://cozystack.cozy-system.svc/repos/extra diff --git a/packages/core/platform/templates/migration-hook.yaml b/packages/core/platform/templates/migration-hook.yaml new file mode 100644 index 00000000..d4b3869e --- /dev/null +++ b/packages/core/platform/templates/migration-hook.yaml @@ -0,0 +1,68 @@ +{{- $shouldRunMigrationHook := false }} +{{- $currentVersion := 0 }} +{{- $targetVersion := .Values.migrations.targetVersion | int }} +{{- $configMap := lookup "v1" "ConfigMap" .Release.Namespace "cozystack-version" }} +{{- if $configMap }} + {{- $currentVersion = dig "data" "version" "0" $configMap | int }} + {{- if lt $currentVersion $targetVersion }} + {{- $shouldRunMigrationHook = true }} + {{- end }} +{{- end }} + +{{- if $shouldRunMigrationHook }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: cozystack-migration-hook + annotations: + helm.sh/hook: pre-upgrade + helm.sh/hook-weight: "1" + helm.sh/hook-delete-policy: before-hook-creation +spec: + backoffLimit: 3 + template: + metadata: + labels: + policy.cozystack.io/allow-to-apiserver: "true" + spec: + serviceAccountName: cozystack-migration-hook + containers: + - name: migration + image: {{ .Values.migrations.image }} + env: + - name: NAMESPACE + value: {{ .Release.Namespace | quote }} + - name: CURRENT_VERSION + value: {{ $currentVersion | quote }} + - name: TARGET_VERSION + value: {{ $targetVersion | quote }} + restartPolicy: Never +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + annotations: + helm.sh/hook: pre-upgrade + helm.sh/hook-weight: "1" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation + name: cozystack-migration-hook +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: cozystack-migration-hook + namespace: {{ .Release.Namespace | quote }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cozystack-migration-hook + annotations: + helm.sh/hook: pre-upgrade + helm.sh/hook-weight: "1" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation +{{- end }} + diff --git a/packages/core/platform/templates/namespaces.yaml b/packages/core/platform/templates/namespaces.yaml deleted file mode 100644 index 11d00553..00000000 --- a/packages/core/platform/templates/namespaces.yaml +++ /dev/null @@ -1,40 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $bundleName := index $cozyConfig.data "bundle-name" }} -{{- $bundle := tpl (.Files.Get (printf "bundles/%s.yaml" $bundleName)) . | fromYaml }} -{{- $disabledComponents := splitList "," ((index $cozyConfig.data "bundle-disable") | default "") }} -{{- $enabledComponents := splitList "," ((index $cozyConfig.data "bundle-enable") | default "") }} -{{- $namespaces := dict }} - -{{/* collect namespaces from releases */}} -{{- range $x := $bundle.releases }} - {{- if not (hasKey $namespaces $x.namespace) }} - {{- if not (has $x.name $disabledComponents) }} - {{- if or (not $x.optional) (and ($x.optional) (has $x.name $enabledComponents)) }} - {{- $_ := set $namespaces $x.namespace false }} - {{- end }} - {{- end }} - {{- end }} - {{/* if at least one release requires a privileged namespace, then it should be privileged */}} - {{- if or $x.privileged (index $namespaces $x.namespace) }} - {{- $_ := set $namespaces $x.namespace true }} - {{- end }} -{{- end }} - -{{/* Add extra namespaces */}} -{{- $_ := set $namespaces "cozy-system" true }} -{{- $_ := set $namespaces "cozy-public" false }} - -{{- range $namespace, $privileged := $namespaces }} ---- -apiVersion: v1 -kind: Namespace -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - cozystack.io/system: "true" - {{- if $privileged }} - pod-security.kubernetes.io/enforce: privileged - {{- end }} - name: {{ $namespace }} -{{- end }} diff --git a/packages/core/platform/templates/platform.yaml b/packages/core/platform/templates/platform.yaml new file mode 100644 index 00000000..d2e798b1 --- /dev/null +++ b/packages/core/platform/templates/platform.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: cozystack-packages + namespace: cozy-system +spec: + interval: 5m0s + url: oci://ghcr.io/cozystack/cozystack/platform-packages + ref: + digest: sha256:ca54e8ef2073ed387aaf74fed4156cee365a7925c3c51174ff5115c95a1b9179 + #tag: latest +--- +apiVersion: source.extensions.fluxcd.io/v1beta1 +kind: ArtifactGenerator +metadata: + name: cozystack-packages + namespace: cozy-system +spec: + artifacts: + - copy: + - from: '@cozystack/core/platform/**' + to: '@artifact/platform/' + name: cozystack-core-platform + sources: + - alias: cozystack + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system diff --git a/packages/core/platform/templates/version.yaml b/packages/core/platform/templates/version.yaml new file mode 100644 index 00000000..ebc5738f --- /dev/null +++ b/packages/core/platform/templates/version.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cozystack-version + namespace: cozy-system +data: + version: "{{ .Values.migrations.targetVersion }}" + diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml new file mode 100644 index 00000000..61869e65 --- /dev/null +++ b/packages/core/platform/values.yaml @@ -0,0 +1,99 @@ +sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system +migrations: + image: ghcr.io/cozystack/cozystack/platform-migrations:latest@sha256:390dff1ac16d99c7677a2b580e0dc41bcb2ffd50232ddf4adff81d6f86aae70b + targetVersion: 23 +# Bundle deployment configuration +bundles: + system: + type: "full" # "hosted", or "minimal" + iaas: + enabled: true + paas: + enabled: true + naas: + enabled: true +# Infrastructure components configuration +# Enable or disable specific components and provide custom values +#components: +# metallb: +# enabled: false +# hetzner-robotlb: +# enabled: true +# cilium: +# enabled: true +# values: +# k8sServiceHost: "" +# k8sServicePort: 6443 +components: {} +# Network configuration +networking: + clusterDomain: "cozy.local" + podCIDR: "10.244.0.0/16" + podGateway: "10.244.0.1" + serviceCIDR: "10.96.0.0/16" + joinCIDR: "100.64.0.0/16" +# Service publishing and ingress configuration +publishing: + host: "example.org" + ingressName: tenant-root + exposedServices: + - api + - dashboard + - vm-exportproxy + - cdi-uploadproxy + apiServerEndpoint: "" # example: "https://api.example.org" + externalIPs: [] + certificates: + issuerType: http01 # "http01" or "cloudflare" +# Authentication configuration +authentication: + oidc: + enabled: false + dashboard: + extraRedirectUris: [] +# Pod scheduling configuration +scheduling: + topologySpreadConstraints: [] +# UI branding configuration +branding: + dashboard: {} + keycloak: {} +# Container registry mirrors configuration +# +# Example: +# registries: +# mirrors: +# docker.io: +# endpoints: +# - http://10.0.0.1:8082 +# ghcr.io: +# endpoints: +# - http://10.0.0.1:8083 +# gcr.io: +# endpoints: +# - http://10.0.0.1:8084 +# registry.k8s.io: +# endpoints: +# - http://10.0.0.1:8085 +# quay.io: +# endpoints: +# - http://10.0.0.1:8086 +# cr.fluentbit.io: +# endpoints: +# - http://10.0.0.1:8087 +# docker-registry3.mariadb.com: +# endpoints: +# - http://10.0.0.1:8088 +# config: +# "10.0.0.1:8082": +# tls: +# insecureSkipVerify: true +registries: {} +# Resource allocation ratios +resources: + cpuAllocationRatio: 10 + memoryAllocationRatio: 1 + ephemeralStorageAllocationRatio: 40 diff --git a/packages/system/cozystack-resource-definition-crd/Chart.yaml b/packages/core/talos/Chart.yaml similarity index 74% rename from packages/system/cozystack-resource-definition-crd/Chart.yaml rename to packages/core/talos/Chart.yaml index 9924c7fa..7c10b8d7 100644 --- a/packages/system/cozystack-resource-definition-crd/Chart.yaml +++ b/packages/core/talos/Chart.yaml @@ -1,3 +1,3 @@ apiVersion: v2 -name: cozystack-resource-definition-crd +name: cozy-talos version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/core/talos/Makefile b/packages/core/talos/Makefile new file mode 100644 index 00000000..03cea20c --- /dev/null +++ b/packages/core/talos/Makefile @@ -0,0 +1,37 @@ +NAME=talos +NAMESPACE=cozy-system + +TALOS_VERSION=$(shell awk '/^version:/ {print $$2}' images/talos/profiles/installer.yaml) + +include ../../../hack/common-envs.mk + +update: + hack/gen-profiles.sh + +image: image-matchbox image-talos + +image-talos: + test -f ../../../_out/assets/installer-amd64.tar || make talos-installer + skopeo copy docker-archive:../../../_out/assets/installer-amd64.tar docker://$(REGISTRY)/talos:$(call settag,$(TALOS_VERSION)) + +image-matchbox: + test -f ../../../_out/assets/kernel-amd64 || make talos-kernel + test -f ../../../_out/assets/initramfs-metal-amd64.xz || make talos-initramfs + docker buildx build -f images/matchbox/Dockerfile ../../.. \ + --tag $(REGISTRY)/matchbox:$(call settag,$(TAG)) \ + --tag $(REGISTRY)/matchbox:$(call settag,$(TALOS_VERSION)-$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/matchbox:latest \ + --cache-to type=inline \ + --metadata-file images/matchbox.json \ + $(BUILDX_ARGS) + echo "$(REGISTRY)/matchbox:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/matchbox.json -o json -r)" \ + > ../../extra/bootbox/images/matchbox.tag + rm -f images/matchbox.json + +assets: talos-iso talos-nocloud talos-metal talos-kernel talos-initramfs + +talos-initramfs talos-kernel talos-installer talos-iso talos-nocloud talos-metal: + mkdir -p ../../../_out/assets + cat images/talos/profiles/$(subst talos-,,$@).yaml | \ + docker run --rm -i -v /dev:/dev --privileged "ghcr.io/siderolabs/imager:$(TALOS_VERSION)" --tar-to-stdout - | \ + tar -C ../../../_out/assets -xzf- diff --git a/packages/core/installer/hack/gen-profiles.sh b/packages/core/talos/hack/gen-profiles.sh similarity index 100% rename from packages/core/installer/hack/gen-profiles.sh rename to packages/core/talos/hack/gen-profiles.sh diff --git a/packages/core/installer/hack/gen-versions.sh b/packages/core/talos/hack/gen-versions.sh similarity index 100% rename from packages/core/installer/hack/gen-versions.sh rename to packages/core/talos/hack/gen-versions.sh diff --git a/packages/core/installer/images/matchbox/Dockerfile b/packages/core/talos/images/matchbox/Dockerfile similarity index 53% rename from packages/core/installer/images/matchbox/Dockerfile rename to packages/core/talos/images/matchbox/Dockerfile index 1083ab26..fbb6f3bf 100644 --- a/packages/core/installer/images/matchbox/Dockerfile +++ b/packages/core/talos/images/matchbox/Dockerfile @@ -2,5 +2,5 @@ FROM quay.io/poseidon/matchbox:v0.10.0 COPY _out/assets/initramfs-metal-amd64.xz /var/lib/matchbox/assets/initramfs.xz COPY _out/assets/kernel-amd64 /var/lib/matchbox/assets/vmlinuz -COPY packages/core/installer/images/matchbox/groups /var/lib/matchbox/groups -COPY packages/core/installer/images/matchbox/profiles /var/lib/matchbox/profiles +COPY packages/core/talos/images/matchbox/groups /var/lib/matchbox/groups +COPY packages/core/talos/images/matchbox/profiles /var/lib/matchbox/profiles diff --git a/packages/core/installer/images/matchbox/groups/default.json b/packages/core/talos/images/matchbox/groups/default.json similarity index 100% rename from packages/core/installer/images/matchbox/groups/default.json rename to packages/core/talos/images/matchbox/groups/default.json diff --git a/packages/core/installer/images/matchbox/profiles/default.json b/packages/core/talos/images/matchbox/profiles/default.json similarity index 100% rename from packages/core/installer/images/matchbox/profiles/default.json rename to packages/core/talos/images/matchbox/profiles/default.json diff --git a/packages/core/installer/images/talos/profiles/initramfs.yaml b/packages/core/talos/images/talos/profiles/initramfs.yaml similarity index 100% rename from packages/core/installer/images/talos/profiles/initramfs.yaml rename to packages/core/talos/images/talos/profiles/initramfs.yaml diff --git a/packages/core/installer/images/talos/profiles/installer.yaml b/packages/core/talos/images/talos/profiles/installer.yaml similarity index 100% rename from packages/core/installer/images/talos/profiles/installer.yaml rename to packages/core/talos/images/talos/profiles/installer.yaml diff --git a/packages/core/installer/images/talos/profiles/iso.yaml b/packages/core/talos/images/talos/profiles/iso.yaml similarity index 100% rename from packages/core/installer/images/talos/profiles/iso.yaml rename to packages/core/talos/images/talos/profiles/iso.yaml diff --git a/packages/core/installer/images/talos/profiles/kernel.yaml b/packages/core/talos/images/talos/profiles/kernel.yaml similarity index 100% rename from packages/core/installer/images/talos/profiles/kernel.yaml rename to packages/core/talos/images/talos/profiles/kernel.yaml diff --git a/packages/core/installer/images/talos/profiles/metal.yaml b/packages/core/talos/images/talos/profiles/metal.yaml similarity index 100% rename from packages/core/installer/images/talos/profiles/metal.yaml rename to packages/core/talos/images/talos/profiles/metal.yaml diff --git a/packages/core/installer/images/talos/profiles/nocloud.yaml b/packages/core/talos/images/talos/profiles/nocloud.yaml similarity index 100% rename from packages/core/installer/images/talos/profiles/nocloud.yaml rename to packages/core/talos/images/talos/profiles/nocloud.yaml diff --git a/packages/core/talos/values.yaml b/packages/core/talos/values.yaml new file mode 100644 index 00000000..e69de29b diff --git a/packages/core/testing/Makefile b/packages/core/testing/Makefile index f345d155..46705011 100755 --- a/packages/core/testing/Makefile +++ b/packages/core/testing/Makefile @@ -6,7 +6,7 @@ SANDBOX_NAME := cozy-e2e-sandbox-$(shell echo "$$(hostname):$$(pwd)" | sha256sum ROOT_DIR = $(dir $(abspath $(firstword $(MAKEFILE_LIST))/../../..)) -include ../../../scripts/common-envs.mk +include ../../../hack/common-envs.mk help: ## Show this help. diff --git a/packages/extra/Makefile b/packages/extra/Makefile index 5872855b..01548800 100644 --- a/packages/extra/Makefile +++ b/packages/extra/Makefile @@ -1,12 +1,7 @@ OUT=../../_out/repos/extra CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}') -include ../../scripts/common-envs.mk - -repo: - rm -rf "$(OUT)" - helm package -d "$(OUT)" $(CHARTS) --version $(COZYSTACK_VERSION) - helm repo index "$(OUT)" +include ../../hack/common-envs.mk fix-charts: find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}' | while read i; do sed -i -e "s/^name: .*/name: $$i/" -e "s/^version: .*/version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process/g" "$$i/Chart.yaml"; done diff --git a/packages/extra/bootbox/Makefile b/packages/extra/bootbox/Makefile index d9a1e261..57fa0a77 100644 --- a/packages/extra/bootbox/Makefile +++ b/packages/extra/bootbox/Makefile @@ -1,7 +1,7 @@ NAME=bootbox NAMESPACE=tenant-root -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/bootbox/templates/matchbox/ingress.yaml b/packages/extra/bootbox/templates/matchbox/ingress.yaml index 31de7716..9a2e31d0 100644 --- a/packages/extra/bootbox/templates/matchbox/ingress.yaml +++ b/packages/extra/bootbox/templates/matchbox/ingress.yaml @@ -1,9 +1,8 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} - -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $issuerType := dig "publishing" "certificates" "issuerType" "http01" $cozystack }} +{{- $namespace := .Values._namespace | default dict }} +{{- $ingress := dig "ingress" "" $namespace }} +{{- $host := dig "host" "" $namespace }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/packages/extra/bootbox/templates/matchbox/machines.yaml b/packages/extra/bootbox/templates/matchbox/machines.yaml index e2733b89..d00dd6a5 100644 --- a/packages/extra/bootbox/templates/matchbox/machines.yaml +++ b/packages/extra/bootbox/templates/matchbox/machines.yaml @@ -1,9 +1,7 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} - -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $namespace := .Values._namespace | default dict }} +{{- $ingress := dig "ingress" "" $namespace }} +{{- $host := dig "host" "" $namespace }} {{ range $m := .Values.machines }} --- diff --git a/packages/extra/etcd/Makefile b/packages/extra/etcd/Makefile index b309346c..f37d6e1e 100644 --- a/packages/extra/etcd/Makefile +++ b/packages/extra/etcd/Makefile @@ -1,6 +1,6 @@ NAME=etcd -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/etcd/templates/etcd-cluster.yaml b/packages/extra/etcd/templates/etcd-cluster.yaml index 017a3178..de884b09 100644 --- a/packages/extra/etcd/templates/etcd-cluster.yaml +++ b/packages/extra/etcd/templates/etcd-cluster.yaml @@ -49,16 +49,12 @@ spec: {{- with .Values.resources }} resources: {{- include "cozy-lib.resources.sanitize" (list . $) | nindent 10 }} {{- end }} - {{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} - {{- $rawConstraints := "" }} - {{- if $configMap }} - {{- $rawConstraints = get $configMap.data "globalAppTopologySpreadConstraints" }} - {{- end }} - {{- if $rawConstraints }} - {{- $rawConstraints | fromYaml | toYaml | nindent 6 }} - labelSelector: - matchLabels: - app.kubernetes.io/instance: etcd + {{- $cozystack := .Values._cozystack | default dict }} + {{- $topologySpreadConstraints := dig "scheduling" "topologySpreadConstraints" (list) $cozystack }} + {{- if $topologySpreadConstraints }} + {{- range $topologySpreadConstraints }} + - {{- mergeOverwrite (dict "labelSelector" (dict "matchLabels" (dict "app.kubernetes.io/instance" "etcd"))) . | toYaml | nindent 8 | trim }} + {{- end }} {{- else }} topologySpreadConstraints: - maxSkew: 1 diff --git a/packages/extra/info/Makefile b/packages/extra/info/Makefile index e09b33a0..02e04d53 100644 --- a/packages/extra/info/Makefile +++ b/packages/extra/info/Makefile @@ -1,6 +1,6 @@ NAME=etcd -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/info/templates/dashboard-resourcemap.yaml b/packages/extra/info/templates/dashboard-resourcemap.yaml index 39da1b37..88603cfc 100644 --- a/packages/extra/info/templates/dashboard-resourcemap.yaml +++ b/packages/extra/info/templates/dashboard-resourcemap.yaml @@ -1,5 +1,5 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $oidcEnabled := index $cozyConfig.data "oidc-enabled" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $oidcEnabled := dig "authentication" "oidc" "enabled" false $cozystack | toString }} apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -10,10 +10,11 @@ rules: resources: - secrets resourceNames: - {{- if eq $oidcEnabled "true" }} + {{- if $oidcEnabled }} - kubeconfig-{{ .Release.Namespace }} {{- else }} - {{ .Release.Namespace }} + - kubeconfig-{{ .Release.Namespace }} {{- end }} verbs: ["get", "list", "watch"] --- @@ -22,7 +23,7 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: {{ .Release.Name }}-dashboard-resources subjects: -{{- if eq $oidcEnabled "true" }} +{{- if $oidcEnabled }} {{ include "cozy-lib.rbac.subjectsForTenantAndAccessLevel" (list "view" .Release.Namespace) }} {{- else }} - kind: ServiceAccount diff --git a/packages/extra/info/templates/kubeconfig.yaml b/packages/extra/info/templates/kubeconfig.yaml index d960a587..4cf14b18 100644 --- a/packages/extra/info/templates/kubeconfig.yaml +++ b/packages/extra/info/templates/kubeconfig.yaml @@ -1,16 +1,6 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $k8sClientSecret := lookup "v1" "Secret" "cozy-keycloak" "k8s-client" }} - -{{- if $k8sClientSecret }} -{{- $apiServerEndpoint := index $cozyConfig.data "api-server-endpoint" }} -{{- $managementKubeconfigEndpoint := default "" (get $cozyConfig.data "management-kubeconfig-endpoint") }} -{{- if and $managementKubeconfigEndpoint (ne $managementKubeconfigEndpoint "") }} -{{- $apiServerEndpoint = $managementKubeconfigEndpoint }} -{{- end }} -{{- $k8sClient := index $k8sClientSecret.data "client-secret-key" | b64dec }} -{{- $rootSaConfigMap := lookup "v1" "ConfigMap" "kube-system" "kube-root-ca.crt" }} -{{- $k8sCa := index $rootSaConfigMap.data "ca.crt" | b64enc }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $host := dig "publishing" "host" "" $cozystack }} +{{- $oidcEnabled := dig "authentication" "oidc" "enabled" false $cozystack | toString }} {{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} {{- $tenantRoot := lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} @@ -18,6 +8,17 @@ {{- $host = $tenantRoot.spec.values.host }} {{- end }} {{- end }} + +{{- $apiServerEndpoint := dig "publishing" "apiServerEndpoint" "" $cozystack }} +{{- if not $apiServerEndpoint }} +{{- $apiServerEndpoint = printf "https://api.%s" $host }} +{{- end }} +{{- $rootSaConfigMap := lookup "v1" "ConfigMap" "kube-system" "kube-root-ca.crt" }} +{{- $k8sCa := index $rootSaConfigMap.data "ca.crt" | b64enc }} + +{{- $k8sClientSecret := lookup "v1" "Secret" "cozy-keycloak" "k8s-client" }} +{{- if and $oidcEnabled $k8sClientSecret }} +{{- $k8sClient := index $k8sClientSecret.data "client-secret-key" | b64dec }} --- apiVersion: v1 kind: Secret @@ -51,4 +52,35 @@ stringData: - --oidc-client-secret={{ $k8sClient }} - --skip-open-browser command: kubectl +{{- else }} +{{- $tenantRootSecret := lookup "v1" "Secret" "tenant-root" "tenant-root" }} +{{- if $tenantRootSecret }} +{{- $caCrt := index $tenantRootSecret.data "ca.crt" }} +{{- $token := index $tenantRootSecret.data "token" | b64dec }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: kubeconfig-{{ .Release.Namespace }} +stringData: + kubeconfig: | + apiVersion: v1 + kind: Config + clusters: + - name: cluster + cluster: + server: {{ $apiServerEndpoint }} + certificate-authority-data: {{ $caCrt }} + contexts: + - name: {{ .Release.Namespace }} + context: + cluster: cluster + namespace: {{ .Release.Namespace }} + user: {{ .Release.Namespace }} + current-context: {{ .Release.Namespace }} + users: + - name: {{ .Release.Namespace }} + user: + token: {{ $token }} +{{- end }} {{- end }} diff --git a/packages/extra/info/templates/serviceaccount.yaml b/packages/extra/info/templates/serviceaccount.yaml index 492f1b27..ee9b7a77 100644 --- a/packages/extra/info/templates/serviceaccount.yaml +++ b/packages/extra/info/templates/serviceaccount.yaml @@ -1,3 +1,6 @@ +{{- $cozystack := .Values._cozystack | default dict }} +{{- $oidcEnabled := dig "authentication" "oidc" "enabled" false $cozystack | toString }} +{{- if not $oidcEnabled }} --- apiVersion: v1 kind: Secret @@ -6,3 +9,4 @@ metadata: annotations: kubernetes.io/service-account.name: {{ .Release.Namespace }} type: kubernetes.io/service-account-token +{{- end }} diff --git a/packages/extra/ingress/Makefile b/packages/extra/ingress/Makefile index 8134cb22..958ce484 100644 --- a/packages/extra/ingress/Makefile +++ b/packages/extra/ingress/Makefile @@ -1,6 +1,6 @@ NAME=ingress -include ../../../scripts/package.mk +include ../../../hack/package.mk update: get-cloudflare-ips diff --git a/packages/extra/ingress/templates/nginx-ingress.yaml b/packages/extra/ingress/templates/nginx-ingress.yaml index e28918e4..4bbcc37b 100644 --- a/packages/extra/ingress/templates/nginx-ingress.yaml +++ b/packages/extra/ingress/templates/nginx-ingress.yaml @@ -1,20 +1,15 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $exposeIngress := index $cozyConfig.data "expose-ingress" | default "tenant-root" }} -{{- $exposeExternalIPs := (index $cozyConfig.data "expose-external-ips") | default "" | nospace }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $exposeIngress := dig "namespaces" "ingress" "tenant-root" $cozystack }} +{{- $exposeExternalIPs := dig "publishing" "externalIPs" (list) $cozystack }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: ingress-nginx-system spec: - chart: - spec: - chart: cozy-ingress-nginx - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-ingress-system + namespace: cozy-system interval: 5m timeout: 10m install: @@ -42,9 +37,9 @@ spec: enabled: false {{- end }} service: - {{- if and (eq $exposeIngress .Release.Namespace) $exposeExternalIPs }} + {{- if and (eq $exposeIngress .Release.Namespace) (gt (len $exposeExternalIPs) 0) }} externalIPs: - {{- toYaml (splitList "," $exposeExternalIPs) | nindent 12 }} + {{- toYaml $exposeExternalIPs | nindent 12 }} type: ClusterIP externalTrafficPolicy: Cluster {{- else }} diff --git a/packages/extra/monitoring/Makefile b/packages/extra/monitoring/Makefile index b21607ab..1ae213b6 100644 --- a/packages/extra/monitoring/Makefile +++ b/packages/extra/monitoring/Makefile @@ -2,8 +2,8 @@ GRAFANA_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) NAME=monitoring -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/monitoring/templates/alerta/alerta-db.yaml b/packages/extra/monitoring/templates/alerta/alerta-db.yaml index a2cca187..0eb909a4 100644 --- a/packages/extra/monitoring/templates/alerta/alerta-db.yaml +++ b/packages/extra/monitoring/templates/alerta/alerta-db.yaml @@ -5,15 +5,13 @@ metadata: name: alerta-db spec: instances: 2 - {{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} - {{- if $configMap }} - {{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }} - {{- if $rawConstraints }} - {{- $rawConstraints | fromYaml | toYaml | nindent 2 }} - labelSelector: - matchLabels: - cnpg.io/cluster: alerta-db - {{- end }} + {{- $cozystack := .Values._cozystack | default dict }} + {{- $topologySpreadConstraints := dig "scheduling" "topologySpreadConstraints" (list) $cozystack }} + {{- if $topologySpreadConstraints }} + topologySpreadConstraints: + {{- range $topologySpreadConstraints }} + - {{- mergeOverwrite (dict "labelSelector" (dict "matchLabels" (dict "cnpg.io/cluster" "alerta-db"))) . | toYaml | nindent 6 | trim }} + {{- end }} {{- end }} storage: size: {{ required ".Values.alerta.storage is required" .Values.alerta.storage }} diff --git a/packages/extra/monitoring/templates/alerta/alerta.yaml b/packages/extra/monitoring/templates/alerta/alerta.yaml index 71336286..a8708a52 100644 --- a/packages/extra/monitoring/templates/alerta/alerta.yaml +++ b/packages/extra/monitoring/templates/alerta/alerta.yaml @@ -1,9 +1,8 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} - -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $issuerType := dig "publishing" "certificates" "issuerType" "http01" $cozystack }} +{{- $namespace := .Values._namespace | default dict }} +{{- $ingress := dig "ingress" "" $namespace }} +{{- $host := dig "host" "" $namespace }} {{- $apiKey := randAlphaNum 32 }} {{- $existingSecret := lookup "v1" "Secret" .Release.Namespace "alerta" }} diff --git a/packages/extra/monitoring/templates/dashboards.yaml b/packages/extra/monitoring/templates/dashboards.yaml index 3facf66c..a4685428 100644 --- a/packages/extra/monitoring/templates/dashboards.yaml +++ b/packages/extra/monitoring/templates/dashboards.yaml @@ -11,6 +11,6 @@ spec: instanceSelector: matchLabels: dashboards: grafana - url: http://cozystack.cozy-system.svc/dashboards/{{ . }}.json + url: http://grafana-dashboards.cozy-grafana-operator.svc/{{ . }}.json {{- end }} {{- end }} diff --git a/packages/extra/monitoring/templates/grafana/db.yaml b/packages/extra/monitoring/templates/grafana/db.yaml index f1781cff..bb90c477 100644 --- a/packages/extra/monitoring/templates/grafana/db.yaml +++ b/packages/extra/monitoring/templates/grafana/db.yaml @@ -6,15 +6,13 @@ spec: instances: 2 storage: size: {{ .Values.grafana.db.size }} - {{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} - {{- if $configMap }} - {{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }} - {{- if $rawConstraints }} - {{- $rawConstraints | fromYaml | toYaml | nindent 2 }} - labelSelector: - matchLabels: - cnpg.io/cluster: grafana-db - {{- end }} + {{- $cozystack := .Values._cozystack | default dict }} + {{- $topologySpreadConstraints := dig "scheduling" "topologySpreadConstraints" (list) $cozystack }} + {{- if $topologySpreadConstraints }} + topologySpreadConstraints: + {{- range $topologySpreadConstraints }} + - {{- mergeOverwrite (dict "labelSelector" (dict "matchLabels" (dict "cnpg.io/cluster" "grafana-db"))) . | toYaml | nindent 6 | trim }} + {{- end }} {{- end }} monitoring: enablePodMonitor: true diff --git a/packages/extra/monitoring/templates/grafana/grafana.yaml b/packages/extra/monitoring/templates/grafana/grafana.yaml index 2397fd10..fbc6a017 100644 --- a/packages/extra/monitoring/templates/grafana/grafana.yaml +++ b/packages/extra/monitoring/templates/grafana/grafana.yaml @@ -1,9 +1,8 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} - -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $issuerType := dig "publishing" "certificates" "issuerType" "http01" $cozystack }} +{{- $namespace := .Values._namespace | default dict }} +{{- $ingress := dig "ingress" "" $namespace }} +{{- $host := dig "host" "" $namespace }} --- apiVersion: grafana.integreatly.org/v1beta1 kind: Grafana diff --git a/packages/extra/seaweedfs/Makefile b/packages/extra/seaweedfs/Makefile index be84180b..eaa1b906 100644 --- a/packages/extra/seaweedfs/Makefile +++ b/packages/extra/seaweedfs/Makefile @@ -1,6 +1,6 @@ NAME=seaweedfs -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/seaweedfs/templates/client/cosi-deployment.yaml b/packages/extra/seaweedfs/templates/client/cosi-deployment.yaml index 5307a67f..c2541b8f 100644 --- a/packages/extra/seaweedfs/templates/client/cosi-deployment.yaml +++ b/packages/extra/seaweedfs/templates/client/cosi-deployment.yaml @@ -1,7 +1,8 @@ {{- if eq .Values.topology "Client" }} -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $namespace := .Values._namespace | default dict }} +{{- $ingress := dig "ingress" "" $namespace }} +{{- $host := dig "host" "" $namespace }} --- apiVersion: apps/v1 kind: Deployment diff --git a/packages/extra/seaweedfs/templates/ingress.yaml b/packages/extra/seaweedfs/templates/ingress.yaml index 05bf201d..fce0997c 100644 --- a/packages/extra/seaweedfs/templates/ingress.yaml +++ b/packages/extra/seaweedfs/templates/ingress.yaml @@ -1,9 +1,8 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} - -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $issuerType := dig "publishing" "certificates" "issuerType" "http01" $cozystack }} +{{- $namespace := .Values._namespace | default dict }} +{{- $ingress := dig "ingress" "" $namespace }} +{{- $host := dig "host" "" $namespace }} {{- if and (not (eq .Values.topology "Client")) (.Values.filer.grpcHost) }} --- apiVersion: networking.k8s.io/v1 diff --git a/packages/extra/seaweedfs/templates/seaweedfs.yaml b/packages/extra/seaweedfs/templates/seaweedfs.yaml index 02bac375..b91be453 100644 --- a/packages/extra/seaweedfs/templates/seaweedfs.yaml +++ b/packages/extra/seaweedfs/templates/seaweedfs.yaml @@ -34,23 +34,19 @@ {{- end }} {{- if not (eq .Values.topology "Client") }} -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} +{{- $cozystack := .Values._cozystack | default dict }} +{{- $namespace := .Values._namespace | default dict }} +{{- $ingress := dig "ingress" "" $namespace }} +{{- $host := dig "host" "" $namespace }} apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: {{ .Release.Name }}-system spec: - chart: - spec: - chart: cozy-seaweedfs - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-seaweedfs-system + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/library/Makefile b/packages/library/Makefile index da811e7e..b16e31b2 100644 --- a/packages/library/Makefile +++ b/packages/library/Makefile @@ -1,12 +1,7 @@ OUT=../../_out/repos/library CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}') -include ../../scripts/common-envs.mk - -repo: - rm -rf "$(OUT)" - helm package -d "$(OUT)" $(CHARTS) --version $(COZYSTACK_VERSION) - helm repo index "$(OUT)" +include ../../hack/common-envs.mk fix-charts: find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}' | while read i; do sed -i -e "s/^name: .*/name: $$i/" -e "s/^version: .*/version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process/g" "$$i/Chart.yaml"; done diff --git a/packages/library/cozy-lib/Makefile b/packages/library/cozy-lib/Makefile index 362bf32c..925f90c1 100644 --- a/packages/library/cozy-lib/Makefile +++ b/packages/library/cozy-lib/Makefile @@ -1,5 +1,5 @@ -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/library/cozy-lib/templates/_cozyconfig.tpl b/packages/library/cozy-lib/templates/_cozyconfig.tpl index 559f7415..e6eb19e0 100644 --- a/packages/library/cozy-lib/templates/_cozyconfig.tpl +++ b/packages/library/cozy-lib/templates/_cozyconfig.tpl @@ -1,7 +1,11 @@ {{- define "cozy-lib.loadCozyConfig" }} {{- include "cozy-lib.checkInput" . }} -{{- if not (hasKey (index . 1) "cozyConfig") }} -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $_ := set (index . 1) "cozyConfig" $cozyConfig }} +{{/* Root context is always the second element of the list */}} +{{- $root := index . 1 }} +{{- $target := index . 1 }} +{{- if not (hasKey $target "cozyConfig") }} +{{/* Use _cozystack values directly, no need for data wrapper */}} +{{- $cozyConfig := $root.Values._cozystack | default dict }} +{{- $_ := set $target "cozyConfig" $cozyConfig }} {{- end }} {{- end }} diff --git a/packages/library/cozy-lib/templates/_network.tpl b/packages/library/cozy-lib/templates/_network.tpl index 4908f7d5..5390dee9 100644 --- a/packages/library/cozy-lib/templates/_network.tpl +++ b/packages/library/cozy-lib/templates/_network.tpl @@ -17,7 +17,9 @@ in use and returns `true`. {{- if not $cozyConfig }} {{- include "cozy-lib.network.defaultDisableLoadBalancerNodePorts" . }} {{- else }} -{{- $enabledComponents := splitList "," ((index $cozyConfig.data "bundle-enable") | default "") }} -{{- not (has "robotlb" $enabledComponents) }} +{{- $components := $cozyConfig.components | default dict }} +{{- $robotlb := index $components "hetzner-robotlb" | default dict }} +{{- $robotlbEnabled := $robotlb.enabled | default false }} +{{- not $robotlbEnabled }} {{- end }} {{- end }} diff --git a/packages/library/cozy-lib/templates/_resources.tpl b/packages/library/cozy-lib/templates/_resources.tpl index cb055ea1..9537b002 100644 --- a/packages/library/cozy-lib/templates/_resources.tpl +++ b/packages/library/cozy-lib/templates/_resources.tpl @@ -14,7 +14,7 @@ {{- if not $cozyConfig }} {{- include "cozy-lib.resources.defaultCpuAllocationRatio" . }} {{- else }} -{{- dig "data" "cpu-allocation-ratio" (include "cozy-lib.resources.defaultCpuAllocationRatio" dict) $cozyConfig }} +{{- dig "resources" "cpuAllocationRatio" (include "cozy-lib.resources.defaultCpuAllocationRatio" dict) $cozyConfig }} {{- end }} {{- end }} @@ -24,7 +24,7 @@ {{- if not $cozyConfig }} {{- include "cozy-lib.resources.defaultMemoryAllocationRatio" . }} {{- else }} -{{- dig "data" "memory-allocation-ratio" (include "cozy-lib.resources.defaultMemoryAllocationRatio" dict) $cozyConfig }} +{{- dig "resources" "memoryAllocationRatio" (include "cozy-lib.resources.defaultMemoryAllocationRatio" dict) $cozyConfig }} {{- end }} {{- end }} @@ -34,7 +34,7 @@ {{- if not $cozyConfig }} {{- include "cozy-lib.resources.defaultEphemeralStorageAllocationRatio" . }} {{- else }} -{{- dig "data" "ephemeral-storage-allocation-ratio" (include "cozy-lib.resources.defaultEphemeralStorageAllocationRatio" dict) $cozyConfig }} +{{- dig "resources" "ephemeralStorageAllocationRatio" (include "cozy-lib.resources.defaultEphemeralStorageAllocationRatio" dict) $cozyConfig }} {{- end }} {{- end }} @@ -85,9 +85,21 @@ devices.com/nvidia: "1" # = limit */}} {{- define "cozy-lib.resources.sanitize" }} -{{- $cpuAllocationRatio := include "cozy-lib.resources.cpuAllocationRatio" . | float64 }} -{{- $memoryAllocationRatio := include "cozy-lib.resources.memoryAllocationRatio" . | float64 }} -{{- $ephemeralStorageAllocationRatio := include "cozy-lib.resources.ephemeralStorageAllocationRatio" . | float64 }} +{{- $cpuAllocationRatioRaw := include "cozy-lib.resources.cpuAllocationRatio" . | float64 }} +{{- $cpuAllocationRatio := $cpuAllocationRatioRaw }} +{{- if eq $cpuAllocationRatio 0.0 }} +{{- $cpuAllocationRatio = 10.0 }} +{{- end }} +{{- $memoryAllocationRatioRaw := include "cozy-lib.resources.memoryAllocationRatio" . | float64 }} +{{- $memoryAllocationRatio := $memoryAllocationRatioRaw }} +{{- if eq $memoryAllocationRatio 0.0 }} +{{- $memoryAllocationRatio = 1.0 }} +{{- end }} +{{- $ephemeralStorageAllocationRatioRaw := include "cozy-lib.resources.ephemeralStorageAllocationRatio" . | float64 }} +{{- $ephemeralStorageAllocationRatio := $ephemeralStorageAllocationRatioRaw }} +{{- if eq $ephemeralStorageAllocationRatio 0.0 }} +{{- $ephemeralStorageAllocationRatio = 40.0 }} +{{- end }} {{- $args := index . 0 }} {{- $output := dict "requests" dict "limits" dict }} {{- if or (hasKey $args "limits") (hasKey $args "requests") }} diff --git a/packages/system/Makefile b/packages/system/Makefile index 2104647b..bc9346a6 100644 --- a/packages/system/Makefile +++ b/packages/system/Makefile @@ -1,12 +1,7 @@ OUT=../../_out/repos/system CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}') -include ../../scripts/common-envs.mk - -repo: - rm -rf "$(OUT)" - helm package -d "$(OUT)" $(CHARTS) --version $(COZYSTACK_VERSION) - cd "$(OUT)" && helm repo index . +include ../../hack/common-envs.mk fix-charts: find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}' | while read i; do sed -i -e "s/^name: .*/name: cozy-$$i/" -e "s/^version: .*/version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process/g" "$$i/Chart.yaml"; done diff --git a/packages/system/bootbox/Makefile b/packages/system/bootbox/Makefile index ce4e1af0..2e666462 100644 --- a/packages/system/bootbox/Makefile +++ b/packages/system/bootbox/Makefile @@ -1,7 +1,7 @@ export NAME=bootbox export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/bootbox/templates/bootbox.yaml b/packages/system/bootbox/templates/bootbox.yaml index 496e42e5..3ea687df 100644 --- a/packages/system/bootbox/templates/bootbox.yaml +++ b/packages/system/bootbox/templates/bootbox.yaml @@ -5,6 +5,9 @@ metadata: helm.sh/resource-policy: keep labels: cozystack.io/ui: "true" + apps.cozystack.io/application.kind: BootBox + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: bootbox name: bootbox namespace: tenant-root spec: @@ -13,9 +16,9 @@ spec: chart: bootbox reconcileStrategy: Revision sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + kind: ExternalArtifact + name: cozystack-system-bootbox + namespace: cozy-system version: '>= 0.0.0-0' interval: 1m0s timeout: 5m0s diff --git a/packages/system/bucket/Makefile b/packages/system/bucket/Makefile index d555b71c..87944b28 100644 --- a/packages/system/bucket/Makefile +++ b/packages/system/bucket/Makefile @@ -2,8 +2,8 @@ S3MANAGER_TAG=v0.5.0 export NAME=s3manager-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: @echo Nothing to update diff --git a/packages/system/bucket/templates/ingress.yaml b/packages/system/bucket/templates/ingress.yaml index d8d659c7..feee9a8c 100644 --- a/packages/system/bucket/templates/ingress.yaml +++ b/packages/system/bucket/templates/ingress.yaml @@ -1,8 +1,10 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }} -{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }} -{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $certificates := $publishing.certificates | default dict }} +{{- $issuerType := $certificates.issuerType | default "http01" }} +{{- $namespace := dig "namespace" (dict) .Values }} +{{- $host := dig "host" "" $namespace }} +{{- $ingress := dig "ingress" "" $namespace }} apiVersion: networking.k8s.io/v1 kind: Ingress diff --git a/packages/system/capi-operator/Makefile b/packages/system/capi-operator/Makefile index dc421cee..9349f549 100644 --- a/packages/system/capi-operator/Makefile +++ b/packages/system/capi-operator/Makefile @@ -5,7 +5,7 @@ export REPO_URL=https://kubernetes-sigs.github.io/cluster-api-operator export CHART_NAME=cluster-api-operator export CHART_VERSION=^0.19 -include ../../../scripts/package.mk +include ../../../hack/package.mk update: clean capi-operator-update rm -rf charts/cluster-api-operator/charts/ diff --git a/packages/system/capi-providers-bootstrap/Makefile b/packages/system/capi-providers-bootstrap/Makefile index f588e439..54d8ca8b 100644 --- a/packages/system/capi-providers-bootstrap/Makefile +++ b/packages/system/capi-providers-bootstrap/Makefile @@ -1,4 +1,4 @@ export NAME=capi-providers-bootstrap export NAMESPACE=cozy-cluster-api -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/capi-providers-core/Makefile b/packages/system/capi-providers-core/Makefile index a9bae779..f5f038c1 100644 --- a/packages/system/capi-providers-core/Makefile +++ b/packages/system/capi-providers-core/Makefile @@ -1,4 +1,4 @@ export NAME=capi-providers-core export NAMESPACE=cozy-cluster-api -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/capi-providers-cpprovider/Makefile b/packages/system/capi-providers-cpprovider/Makefile index 06f0dd6d..caec8d96 100644 --- a/packages/system/capi-providers-cpprovider/Makefile +++ b/packages/system/capi-providers-cpprovider/Makefile @@ -1,4 +1,4 @@ export NAME=capi-providers-cpprovider export NAMESPACE=cozy-cluster-api -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/capi-providers-infraprovider/Makefile b/packages/system/capi-providers-infraprovider/Makefile index 8e4423f3..042a4f0e 100644 --- a/packages/system/capi-providers-infraprovider/Makefile +++ b/packages/system/capi-providers-infraprovider/Makefile @@ -1,4 +1,4 @@ export NAME=capi-providers-infraprovider export NAMESPACE=cozy-cluster-api -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/cert-manager-crds/Makefile b/packages/system/cert-manager-crds/Makefile index 0d665914..48e5644c 100644 --- a/packages/system/cert-manager-crds/Makefile +++ b/packages/system/cert-manager-crds/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/cert-manager-issuers/Makefile b/packages/system/cert-manager-issuers/Makefile index a7a6ce10..8808dbfd 100644 --- a/packages/system/cert-manager-issuers/Makefile +++ b/packages/system/cert-manager-issuers/Makefile @@ -1,4 +1,4 @@ export NAME=cert-manager-issuers export NAMESPACE=cozy-cert-manager -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml b/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml index 6a70eef0..09cd0788 100644 --- a/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml +++ b/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml @@ -1,5 +1,7 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $certificates := $publishing.certificates | default dict }} +{{- $issuerType := $certificates.issuerType | default "http01" }} apiVersion: cert-manager.io/v1 kind: ClusterIssuer diff --git a/packages/system/cert-manager/Makefile b/packages/system/cert-manager/Makefile index d48ad5be..2d256086 100644 --- a/packages/system/cert-manager/Makefile +++ b/packages/system/cert-manager/Makefile @@ -1,7 +1,7 @@ export NAME=cert-manager export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/cilium-networkpolicy/Makefile b/packages/system/cilium-networkpolicy/Makefile index c81b87e9..a24a54b5 100644 --- a/packages/system/cilium-networkpolicy/Makefile +++ b/packages/system/cilium-networkpolicy/Makefile @@ -1,5 +1,5 @@ export NAME=cilium-networkpolicy export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk diff --git a/packages/system/cilium/Makefile b/packages/system/cilium/Makefile index 269fb285..c22568e9 100644 --- a/packages/system/cilium/Makefile +++ b/packages/system/cilium/Makefile @@ -3,8 +3,8 @@ CILIUM_TAG=$(shell awk '$$1 == "version:" {print $$2}' charts/cilium/Chart.yaml) export NAME=cilium export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/clickhouse-operator/Makefile b/packages/system/clickhouse-operator/Makefile index e821b664..289631d6 100644 --- a/packages/system/clickhouse-operator/Makefile +++ b/packages/system/clickhouse-operator/Makefile @@ -1,7 +1,7 @@ export NAME=clickhouse-operator export NAMESPACE=cozy-clickhouse-operator -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/coredns/Makefile b/packages/system/coredns/Makefile index a610a862..4aca5b38 100644 --- a/packages/system/coredns/Makefile +++ b/packages/system/coredns/Makefile @@ -1,7 +1,7 @@ export NAME=coredns export NAMESPACE=kube-system -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/cozy-proxy/Makefile b/packages/system/cozy-proxy/Makefile index 8f4f6192..c1da95c7 100644 --- a/packages/system/cozy-proxy/Makefile +++ b/packages/system/cozy-proxy/Makefile @@ -1,8 +1,8 @@ NAME=cozy-proxy NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/cozystack-api/Makefile b/packages/system/cozystack-api/Makefile index a3e0d931..c4e6f459 100644 --- a/packages/system/cozystack-api/Makefile +++ b/packages/system/cozystack-api/Makefile @@ -1,8 +1,8 @@ NAME=cozystack-api NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk run-local: openssl req -nodes -new -x509 -keyout /tmp/ca.key -out /tmp/ca.crt -subj "/CN=kube-ca" diff --git a/packages/system/cozystack-api/images/cozystack-api/Dockerfile b/packages/system/cozystack-api/images/cozystack-api/Dockerfile index ea7bdc10..b4b206a6 100644 --- a/packages/system/cozystack-api/images/cozystack-api/Dockerfile +++ b/packages/system/cozystack-api/images/cozystack-api/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24-alpine AS builder +FROM golang:1.25-alpine AS builder ARG TARGETOS ARG TARGETARCH diff --git a/packages/system/cozystack-api/templates/api-ingress.yaml b/packages/system/cozystack-api/templates/api-ingress.yaml index 54fdf54b..f3352d8d 100644 --- a/packages/system/cozystack-api/templates/api-ingress.yaml +++ b/packages/system/cozystack-api/templates/api-ingress.yaml @@ -1,9 +1,13 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $exposeServices := splitList "," ((index $cozyConfig.data "expose-services") | default "") }} -{{- $exposeIngress := index $cozyConfig.data "expose-ingress" | default "tenant-root" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $host := $publishing.host | default "" }} +{{- $exposeServices := $publishing.exposedServices | default list }} +{{- $exposeIngress := $publishing.ingressClassName | default "tenant-root" }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} -{{- if and (has "api" $exposeServices) }} +{{- if has "api" $exposeServices }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/packages/system/cozystack-controller/Makefile b/packages/system/cozystack-controller/Makefile index 9bbfb5a6..117e26df 100644 --- a/packages/system/cozystack-controller/Makefile +++ b/packages/system/cozystack-controller/Makefile @@ -1,10 +1,10 @@ NAME=cozystack-controller NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk -image: image-cozystack-controller update-version +image: image-cozystack-controller image-cozystack-controller: docker buildx build -f images/cozystack-controller/Dockerfile ../../.. \ @@ -16,7 +16,3 @@ image-cozystack-controller: IMAGE="$(REGISTRY)/cozystack-controller:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/cozystack-controller.json -o json -r)" \ yq -i '.cozystackController.image = strenv(IMAGE)' values.yaml rm -f images/cozystack-controller.json - -update-version: - TAG="$(call settag,$(TAG))" \ - yq -i '.cozystackController.cozystackVersion = strenv(TAG)' values.yaml diff --git a/packages/system/cozystack-controller/images/cozystack-controller/Dockerfile b/packages/system/cozystack-controller/images/cozystack-controller/Dockerfile index c7ada9ef..bd6cfb38 100644 --- a/packages/system/cozystack-controller/images/cozystack-controller/Dockerfile +++ b/packages/system/cozystack-controller/images/cozystack-controller/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24-alpine AS builder +FROM golang:1.25-alpine AS builder ARG TARGETOS ARG TARGETARCH diff --git a/packages/system/cozystack-controller/templates/deployment.yaml b/packages/system/cozystack-controller/templates/deployment.yaml index 6dc21b1c..175aa5ca 100644 --- a/packages/system/cozystack-controller/templates/deployment.yaml +++ b/packages/system/cozystack-controller/templates/deployment.yaml @@ -19,15 +19,11 @@ spec: - name: cozystack-controller image: "{{ .Values.cozystackController.image }}" args: - - --cozystack-version={{ .Values.cozystackController.cozystackVersion }} {{- if .Values.cozystackController.debug }} - --zap-log-level=debug {{- else }} - --zap-log-level=info {{- end }} - {{- if .Values.cozystackController.disableTelemetry }} - - --disable-telemetry - {{- end }} {{- if eq .Values.cozystackController.cozystackAPIKind "Deployment" }} - --reconcile-deployment {{- end }} diff --git a/packages/system/cozystack-resource-definition-crd/Makefile b/packages/system/cozystack-resource-definition-crd/Makefile deleted file mode 100644 index ddb24aef..00000000 --- a/packages/system/cozystack-resource-definition-crd/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -export NAME=cozystack-resource-definition-crd -export NAMESPACE=cozy-system - -include ../../../scripts/package.mk diff --git a/packages/system/cozystack-resource-definition-crd/templates/crd.yaml b/packages/system/cozystack-resource-definition-crd/templates/crd.yaml deleted file mode 100644 index 40a93ad3..00000000 --- a/packages/system/cozystack-resource-definition-crd/templates/crd.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -{{ .Files.Get "definition/cozystack.io_cozystackresourcedefinitions.yaml" }} diff --git a/packages/system/cozystack-resource-definition-crd/values.yaml b/packages/system/cozystack-resource-definition-crd/values.yaml deleted file mode 100644 index 0967ef42..00000000 --- a/packages/system/cozystack-resource-definition-crd/values.yaml +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/system/cozystack-resource-definitions/Makefile b/packages/system/cozystack-resource-definitions/Makefile deleted file mode 100644 index 00e8bece..00000000 --- a/packages/system/cozystack-resource-definitions/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -export NAME=cozystack-resource-definitions -export NAMESPACE=cozy-system - -include ../../../scripts/package.mk diff --git a/packages/system/cozystack-resource-definitions/templates/cozyrds.yaml b/packages/system/cozystack-resource-definitions/templates/cozyrds.yaml deleted file mode 100644 index e079ddcf..00000000 --- a/packages/system/cozystack-resource-definitions/templates/cozyrds.yaml +++ /dev/null @@ -1,4 +0,0 @@ -{{- range $path, $_ := .Files.Glob "cozyrds/*" }} ---- -{{ $.Files.Get $path }} -{{- end }} diff --git a/packages/system/cozystack-resource-definitions/values.yaml b/packages/system/cozystack-resource-definitions/values.yaml deleted file mode 100644 index 0967ef42..00000000 --- a/packages/system/cozystack-resource-definitions/values.yaml +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/system/dashboard/Makefile b/packages/system/dashboard/Makefile index f89cba0d..a9f680a3 100644 --- a/packages/system/dashboard/Makefile +++ b/packages/system/dashboard/Makefile @@ -1,8 +1,8 @@ export NAME=dashboard export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: update-crd update-dockerfiles image: image-openapi-ui image-openapi-ui-k8s-bff image-token-proxy update-tenant-text diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index bbf71610..4abc2641 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,4 +1,6 @@ -{{- $brandingConfig:= lookup "v1" "ConfigMap" "cozy-system" "cozystack-branding" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $branding := $cozystack.branding | default dict }} +{{- $dashboard := $branding.dashboard | default dict }} {{- $tenantText := "v0.38.2" }} {{- $footerText := "Cozystack" }} @@ -7,6 +9,13 @@ {{- $logoSvg := "PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjMwIiB2aWV3Qm94PSIwIDAgMTUwIDMwIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Ik0xMzMuMzMxMDkgMjIuMzczOTg3VjQuNzk1Mjc2OWgyLjA0NDUyVjEyLjc3Mzg5aC4wNDk5bDguNTI3NzEtNy45NzkxNjcyaDIuNjE4MjdsLTkuNzc0NjQgOS4xMDExNTgyLjAyNDktMS4wOTcwNTkgMTAuMjk4MjQgOS41NzQ4ODloLTIuNjkyNzlsLTkuMDAxNjktOC4yNzgzNjNoLS4wNDk5djguMjc4MzYzem0tOS4xNzIzNi4yMjQzOTljLTEuNzI4NjkuMC0zLjIwODA1LS4zNjU2ODYtNC40MzgxLTEuMDk3MDU5LTEuMjMwNTktLjczMTM3My0yLjE3ODA0LTEuNzcwMjU0LTIuODQyOTMtMy4xMTY2NDUtLjY0ODI3LTEuMzQ2NjY3LS45NzIzOS0yLjk1MDk3OC0uOTcyMzktNC44MTI2NTQuMC0xLjg2MTY3Ny4zMjQxMi0zLjQ1NzM5OS45NzIzOS00Ljc4NzQ0NS42NjQ4OS0xLjM0NjM5MDYgMS42MTIzNC0yLjM4NTI3MjUgMi44NDI2Ni0zLjExNjkyMjQgMS4yMzAwMy0uNzMxMzcyOSAyLjcwOTQxLTEuMDk3MDU5MiA0LjQzODM3LTEuMDk3MDU5MiAxLjIxMzQyLjAgMi4zMzU0MS4xOTExNTQzIDMuMzY2MjYuNTczNDYyOCAxLjAzMDU3LjM4MjMwODUgMS44OTQ5My45MzkxNDkxIDIuNTkzMDUgMS42NzA1MjE5bC0uNzk3ODYgMS42NzA1MjE5Yy0uNzY0NjItLjcxNDc1LTEuNTYyNDgtMS4yMzAwMzYtMi4zOTM1OS0xLjU0NTg1NjEtLjgxNDQ3LS4zMzI0NDIxLTEuNzIwMzgtLjQ5ODY2MzItMi43MTc3MS0uNDk4NjYzMi0xLjk3ODU5LjAtMy40OTEyLjYyMzMyOS00LjUzODM4IDEuODY5OTg4My0xLjA0NzIxIDEuMjQ2NjU3LTEuNTcwOCAzLjAwMDU2Ni0xLjU3MDggNS4yNjE0NTEuMCAyLjI2MDYwNi41MjM1OSA0LjAyMjU0OSAxLjU3MDggNS4yODYxMDggMS4wNDcxOCAxLjI0NjY1NyAyLjU1OTc5IDEuODY5OTg2IDQuNTM4MDkgMS44Njk5ODYuOTk3MzQuMCAxLjkwMzI0LS4xNTc5MDkgMi43MTc3My0uNDczNzMuODMxMzgtLjMzMjQ0MiAxLjYyOTI0LS44NTYwMzggMi4zOTM4Ni0xLjU3MDc4OWwuNzk3ODYgMS42NzA1MjJjLS42OTgxMi43MTQ3NTEtMS41NjI0OCAxLjI3MTg2OS0yLjU5MzA1IDEuNjcwNzk5LTEuMDMwNTguMzgyMzA5LTIuMTUyNTcuNTczNDYyLTMuMzY1OTcuNTczNDYyek05Ni45ODQ2MzEgMjIuMzczOTg3IDEwNC43Mzk0IDQuNzk0OTk5OWgxLjc0NTMzbDcuNzU0NzYgMTcuNTc4OTg3MWgtMi4xMTkzMmwtMi4xNjkxOS01LjAxMTg0MS45OTczMy41MjM1OTVoLTEwLjcyMjA5bDEuMDIyMjctLjUyMzU5NS0yLjE0NDI1NCA1LjAxMTg0MXptOC42MDI0OTktMTUuMTg1NDAzNC00LjAxNDI0IDkuNDUwNTAwNC0uNTk4NC0uNDczNzNoOS4yMjU1NWwtLjU0ODUzLjQ3MzczLTQuMDE0MjUtOS40NTA1MDA0ek04OS44OTM5MjEgMjIuMzczOTg3VjYuNTY1NTMxNkg4My41MTAyMDJWNC43OTUyNzY5aDE0LjgzNjMzVjYuNTY1NTMxNkg5MS45NjMzNjZWMjIuMzc0MjY1eiIgZmlsbD17dG9rZW4uY29sb3JUZXh0fT48L3BhdGg+CiAgPHBhdGggZD0ibTY3Ljg1NDM4NSA0Ljc3MzExNDJoMTQuMDgwODd2MS43NjAwMDQyaC0xNC4wODA4N3ptMCAxNS44NDA4Njk4aDE0LjA4MDg3djEuNzYwMDAzaC0xNC4wODA4N3ptMTQuMDgwODctNy45MjA0MzVoLTE0LjA4MDg3djEuNzYwMDA1aDE0LjA4MDg3eiIgZmlsbD17dG9rZW4uY29sb3JUZXh0fT48L3BhdGg+CiAgPHBhdGggZD0ibTU3LjY2NDQ3MyAyMi4zNzM5ODd2LTkuMTAxMTU4bC40NDg4MDQgMS40MjExOTEtNy4xODEzMDktOS44OTkwMjAxaDIuMzkzODYxbDUuNjYwMTA5IDcuODI5NTY3MWgtLjUyMzYwNmw1LjY2MDM5MS03LjgyOTU2NzFoMi4zMTg3ODdsLTcuMTU2MzczIDkuODk4NzQyMS40MjQxNC0xLjQyMTE4OXY5LjEwMTE1OHptLTIwLjE4ODEwMi4wVjIwLjg1MzA2NUw0OC4xNDg1OTYgNS43NjczOTMydi43OTc4NjEzSDM3LjQ3NjM3MVY0Ljc5NDk5OTlINTAuMDY4NDVWNi4zMTU5MjI4TDM5LjM5NjUwMSAyMS4zNzY2NjF2LS43NzI5MjdoMTEuMDIxMjl2MS43NzAyNTN6bS0xMC4zOTYyOTguMjI0Mzk5Yy0xLjIxMzQxNC4wLTIuMzE5MDYyLS4yMDc3NzYtMy4zMTYzODktLjYyMzMyOC0uOTk3MzI3LS40MzIxNzUtMS44NDUwNTUtMS4wMzg4ODItMi41NDMxODQtMS44MjAxMjEtLjY5ODQwNi0uNzgxMjM5LTEuMjM4NjI0LTEuNzI4Ny0xLjYyMDkzMy0yLjg0MjY1OC0uMzY1Njg2LTEuMTEzNjgyLS41NDg1MjktMi4zNjAzMzktLjU0ODUyOS0zLjc0MDI1MS4wLTEuMzk2MjU3LjE4Mjg0My0yLjY0MjkxNy41NDg1MjktMy43NDAyNTIuMzgyMzA5LTEuMTEzNjgyLjkyMjUyNy0yLjA1MjgzMDkgMS42MjA2NTYtMi44MTc0NDc1LjY5ODEyOS0uNzgxMjM5MiAxLjUzNzU0NS0xLjM3OTkxMjEgMi41MTgyNS0xLjc5NTQ2NDguOTk3NjA0LS40MzIxNzQ5IDIuMTExMjg3LS42NDgyNjIzIDMuMzQxNi0uNjQ4MjYyMyAxLjI0NjY1Ny4wIDIuMzYwMzM5LjIwNzc3NjMgMy4zNDEwNDQuNjIzMzI5MS45OTczMjcuNDE1NTUyNyAxLjg0NTA1NSAxLjAxMzk0ODYgMi41NDM0NTkgMS43OTUxODc3LjcxNDc1MS43ODEyMzk4IDEuMjU0OTY5IDEuNzI4Njk5OCAxLjYyMDY1NSAyLjg0MjY1NzguMzgyMzEgMS4wOTcwNTkuNTczNDY0IDIuMzM1NDA2LjU3MzQ2NCAzLjcxNTA0Mi4wIDEuMzk2NTMzLS4xOTExNTQgMi42NTE3NzktLjU3MzQ2NCAzLjc2NTQ2MS0uMzgyMzA4IDEuMTEzNjgyLS45MjI1MjYgMi4wNjExNDItMS42MjA2NTUgMi44NDIzODFzLTEuNTQ1ODU2IDEuMzg3OTQ2LTIuNTQzMTgzIDEuODIwMzk4Yy0uOTgwNzA0LjQxNTU1Mi0yLjA5NDY2My42MjMzMjgtMy4zNDEzMi42MjMzMjh6bTAtMS44MjAxMmMxLjI2MzI3OS4wIDIuMzI3MDk0LS4yODI1NzYgMy4xOTE0NDMtLjg0NzcyOC44ODA5NzMtLjU2NTE1MiAxLjU1NDE2OS0xLjM4Nzk0NiAyLjAxOTU4OC0yLjQ2ODY2LjQ2NTY5Ni0xLjA4MDQzNy42OTg0MDYtMi4zNzY5NjEuNjk4NDA2LTMuODg5ODUuMC0xLjUyOTIzNC0uMjMyNzEtMi44MjU3NTgtLjY5ODEyOS0zLjg4OTg1MS0uNDY1NDE5LTEuMDYzODE1LTEuMTM4NjE0LTEuODc4Mjk3OC0yLjAxOTU4Ny0yLjQ0MzQ1LS44NjQzNS0uNTY1MTUxOC0xLjkyODQ0Mi0uODQ3NzI3Ni0zLjE5MTcyMS0uODQ3NzI3Ni0xLjIzMDAzOC4wLTIuMjg1ODE4LjI4MjU3NTgtMy4xNjY3OTEuODQ3NzI3Ni0uODY0MzUuNTY1MTUyMi0xLjUyOTIzNCAxLjM4Nzk0Ni0xLjk5NDY1MyAyLjQ2ODM4My0uNDY1NDE5IDEuMDYzODE1LS42OTgxMjkgMi4zNTIwMjktLjY5ODEyOSAzLjg2NDY0MS4wIDEuNTEyODg5LjIzMjcxIDIuODA5NjkuNjk4MTI5IDMuODkwMTI3LjQ2NTQxOSAxLjA2MzgxNSAxLjEzMDMwMyAxLjg4NjYwOSAxLjk5NDY1MyAyLjQ2ODM4My44ODA5NzMuNTY1MTUxIDEuOTM2NDc4Ljg0NzcyNyAzLjE2NjUxMy44NDc3Mjd6bS0xNi4wNDE3MjQgMS44MjAxMmMtMS43Mjg2OTgxLjAtMy4yMDgzNDE3LS4zNjU2ODYtNC40Mzg2NTYxLTEuMDk3MDU5QzUuMzY5NjU3MSAyMC43Njk5NTQgNC40MjIxOTY2IDE5LjczMTA3MyAzLjc1NzMxMzEgMTguMzg0NjgyYy0uNjQ4MjYzLTEuMzQ2NjY3LS45NzIzOTM2LTIuOTUwOTc4LS45NzIzOTM2LTQuODEyNjU0LjAtMS44NjE2NzcuMzI0MTMwNi0zLjQ1NzM5OS45NzIzOTM2LTQuNzg3NDQ1LjY2NDg4MzUtMS4zNDYzOTA2IDEuNjEyMzQ0LTIuMzg1MjcyNSAyLjg0MjM3OTgtMy4xMTY5MjI0QzcuODI5NzI5OCA0LjkzNjI4NzcgOS4zMDkzNzM0IDQuNTcwNjAxNCAxMS4wMzgzNDkgNC41NzA2MDE0YzEuMjEzNDE0LjAgMi4zMzU0MDguMTkxMTU0MyAzLjM2NTk3OC41NzM0NjI4IDEuMDMwNTcyLjM4MjMwODUgMS44OTQ5MjIuOTM5MTQ5MSAyLjU5MzMyNiAxLjY3MDUyMTlMMTYuMTk5NzkxIDguNDg1MTA4QzE1LjQzNTE3NSA3Ljc3MDM1OCAxNC42MzczMTMgNy4yNTUwNzIgMTMuODA2MjA5IDYuOTM5MjUxOSAxMi45OTE0NDcgNi42MDY4MDk4IDEyLjA4NTU0MyA2LjQ0MDU4ODcgMTEuMDg4MjE2IDYuNDQwNTg4N2MtMS45NzgwMzA0LjAtMy40OTA5MTg4LjYyMzMyOS00LjUzODM4OTIgMS44Njk5ODgzLTEuMDQ3MTkyNyAxLjI0NjY1Ny0xLjU3MDc4ODYgMy4wMDA1NjYtMS41NzA3ODg2IDUuMjYxNDUxLjAgMi4yNjA2MDYuNTIzNTk1OSA0LjAyMjU0OSAxLjU3MDc4ODYgNS4yODYxMDggMS4wNDcxOTI5IDEuMjQ2NjU3IDIuNTYwMDgyMyAxLjg2OTk4NiA0LjUzODM4OTIgMS44Njk5ODYuOTk3MzI3LjAgMS45MDMyMzEtLjE1NzkwOSAyLjcxNzcxNS0uNDczNzMuODMxMTA2LS4zMzI0NDIgMS42Mjg5NjgtLjg1NjAzOCAyLjM5MzU4NC0xLjU3MDc4OWwuNzk4MTM4IDEuNjcwNTIyYy0uNjk4MTI4LjcxNDc1MS0xLjU2MjQ3OCAxLjI3MTg2OS0yLjU5MzA0OCAxLjY3MDc5OS0xLjAzMDU3Mi4zODIzMDktMi4xNTI4NDIuNTczNDYyLTMuMzY2MjU2LjU3MzQ2MnoiIGZpbGw9e3Rva2VuLmNvbG9yVGV4dH0+PC9wYXRoPgo8L3N2Zz4K" }} {{- $iconSvg := "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgd2lkdGg9IjkxIgogICBoZWlnaHQ9IjkxIgogICB2aWV3Qm94PSIwIDAgNjguMjUwMDI0IDY4LjI1MDAyNCIKICAgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgbWVldCIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnODI2IgogICBzb2RpcG9kaTpkb2NuYW1lPSJmYXZpY29uLnN2ZyIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMS4xLjEgKGMzMDg0ZWYsIDIwMjEtMDktMjIpIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM4MzAiIC8+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJuYW1lZHZpZXc4MjgiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZWNoZWNrZXJib2FyZD0iMCIKICAgICBzaG93Z3JpZD0iZmFsc2UiCiAgICAgaW5rc2NhcGU6em9vbT0iMC43NzAzNTc0MSIKICAgICBpbmtzY2FwZTpjeD0iNDM2LjgxMDIzIgogICAgIGlua3NjYXBlOmN5PSI1NDEuOTU2MjMiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNzIwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjEzODciCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjE3MjAiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9IjI1IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjAiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnODI2IiAvPgogIDxyZWN0CiAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtzdHJva2Utd2lkdGg6MC42MTc5MDUiCiAgICAgaWQ9InJlY3Q5MzgiCiAgICAgd2lkdGg9IjY4LjI1MDAyMyIKICAgICBoZWlnaHQ9IjY4LjI1MDAyMyIKICAgICB4PSIwIgogICAgIHk9Ii0xLjc3NjM1NjhlLTE1IiAvPgogIDxwYXRoCiAgICAgZmlsbC1ydWxlPSJldmVub2RkIgogICAgIGNsaXAtcnVsZT0iZXZlbm9kZCIKICAgICBkPSJtIDE2LjYwMTU5OCwxMy45MjY5MSBjIDAsLTEuMjgwMTE1IDEuMDE1MDUxLC0yLjMxNzg2MSAyLjI2NzQyNCwtMi4zMTc4NjEgaCAzMS43NDM5MzcgYyAxLjI1MjM3OCwwIDIuMjY3NDIsMS4wMzc3NDYgMi4yNjc0MiwyLjMxNzg2MSB2IDAgYyAwLDEuMjgwMTMzIC0xLjAxNTA0MiwyLjMxNzg3IC0yLjI2NzQyLDIuMzE3ODcgSCAxOC44NjkwMjIgYyAtMS4yNTIzNzMsMCAtMi4yNjc0MjQsLTEuMDM3NzM3IC0yLjI2NzQyNCwtMi4zMTc4NyB6IG0gMCw0MS43MjE1NzIgYyAwLC0xLjI4MDA4MSAxLjAxNTA1MSwtMi4zMTc4NjUgMi4yNjc0MjQsLTIuMzE3ODY1IGggMzEuNzQzOTM3IGMgMS4yNTIzNzgsMCAyLjI2NzQyLDEuMDM3Nzg0IDIuMjY3NDIsMi4zMTc4NjUgdiAwIGMgMCwxLjI4MDE1OCAtMS4wMTUwNDIsMi4zMTc4NjYgLTIuMjY3NDIsMi4zMTc4NjYgSCAxOC44NjkwMjIgYyAtMS4yNTIzNzMsMCAtMi4yNjc0MjQsLTEuMDM3NzA4IC0yLjI2NzQyNCwtMi4zMTc4NjYgeiBtIDM2LjI3ODc4MSwtMjAuODYwOCBjIDAsLTEuMjgwMDggLTEuMDE1MDQyLC0yLjMxNzg2NSAtMi4yNjc0MiwtMi4zMTc4NjUgSCAxOC44NjkwMjIgYyAtMS4yNTIzNzMsMCAtMi4yNjc0MjQsMS4wMzc3ODUgLTIuMjY3NDI0LDIuMzE3ODY1IHYgMCBjIDAsMS4yODAxNTggMS4wMTUwNTEsMi4zMTc4NjYgMi4yNjc0MjQsMi4zMTc4NjYgaCAzMS43NDM5MzcgYyAxLjI1MjM3OCwwIDIuMjY3NDIsLTEuMDM3NzA4IDIuMjY3NDIsLTIuMzE3ODY2IHoiCiAgICAgZmlsbD0iI2ZmZmZmZiIKICAgICBpZD0icGF0aDg0MCIKICAgICBzdHlsZT0ic3Ryb2tlLXdpZHRoOjAuNzY0MTYzIiAvPgo8L3N2Zz4K" }} +{{- if $dashboard.tenantText }}{{- $tenantText = $dashboard.tenantText }}{{- end }} +{{- if $dashboard.footerText }}{{- $footerText = $dashboard.footerText }}{{- end }} +{{- if $dashboard.titleText }}{{- $titleText = $dashboard.titleText }}{{- end }} +{{- if $dashboard.logoText }}{{- $logoText = $dashboard.logoText }}{{- end }} +{{- if $dashboard.logoSvg }}{{- $logoSvg = $dashboard.logoSvg }}{{- end }} +{{- if $dashboard.iconSvg }}{{- $iconSvg = $dashboard.iconSvg }}{{- end }} + --- apiVersion: v1 kind: ConfigMap @@ -16,9 +25,9 @@ metadata: app.kubernetes.io/instance: incloud-web app.kubernetes.io/name: web data: - CUSTOM_TENANT_TEXT: {{ $brandingConfig | dig "data" "tenantText" $tenantText | quote }} - FOOTER_TEXT: {{ $brandingConfig | dig "data" "footerText" $footerText | quote }} - TITLE_TEXT: {{ $brandingConfig | dig "data" "titleText" $titleText | quote }} - LOGO_TEXT: {{ $brandingConfig | dig "data" "logoText" $logoText | quote }} - CUSTOM_LOGO_SVG: {{ $brandingConfig | dig "data" "logoSvg" $logoSvg | quote }} - ICON_SVG: {{ $brandingConfig | dig "data" "iconSvg" $iconSvg | quote }} + CUSTOM_TENANT_TEXT: {{ dig "tenantText" $tenantText $dashboard | quote }} + FOOTER_TEXT: {{ dig "footerText" $footerText $dashboard | quote }} + TITLE_TEXT: {{ dig "titleText" $titleText $dashboard | quote }} + LOGO_TEXT: {{ dig "logoText" $logoText $dashboard | quote }} + CUSTOM_LOGO_SVG: {{ dig "logoSvg" $logoSvg $dashboard | quote }} + ICON_SVG: {{ dig "iconSvg" $iconSvg $dashboard | quote }} diff --git a/packages/system/dashboard/templates/gatekeeper.yaml b/packages/system/dashboard/templates/gatekeeper.yaml index b4503f4f..b494e313 100644 --- a/packages/system/dashboard/templates/gatekeeper.yaml +++ b/packages/system/dashboard/templates/gatekeeper.yaml @@ -1,6 +1,12 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $oidcEnabled := index $cozyConfig.data "oidc-enabled" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $authentication := $cozystack.authentication | default dict }} +{{- $oidc := $authentication.oidc | default dict }} +{{- $host := $publishing.host | default "" }} +{{- $oidcEnabled := $oidc.enabled | default false }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} apiVersion: apps/v1 kind: Deployment @@ -45,7 +51,7 @@ spec: restartPolicy: Always priorityClassName: system-cluster-critical containers: - {{- if eq $oidcEnabled "true" }} + {{- if $oidcEnabled }} - name: auth-proxy image: quay.io/oauth2-proxy/oauth2-proxy:v7.12.0 imagePullPolicy: IfNotPresent diff --git a/packages/system/dashboard/templates/ingress.yaml b/packages/system/dashboard/templates/ingress.yaml index 5a634e2c..cb2730f8 100644 --- a/packages/system/dashboard/templates/ingress.yaml +++ b/packages/system/dashboard/templates/ingress.yaml @@ -1,10 +1,15 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $exposeServices := splitList "," ((index $cozyConfig.data "expose-services") | default "") }} -{{- $exposeIngress := index $cozyConfig.data "expose-ingress" | default "tenant-root" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $certificates := $publishing.certificates | default dict }} +{{- $issuerType := $certificates.issuerType | default "http01" }} +{{- $host := $publishing.host | default "" }} +{{- $exposeServices := $publishing.exposedServices | default list }} +{{- $exposeIngress := $publishing.ingressClassName | default "tenant-root" }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} -{{- if and (has "dashboard" $exposeServices) }} +{{- if has "dashboard" $exposeServices }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/packages/system/dashboard/templates/keycloakclient.yaml b/packages/system/dashboard/templates/keycloakclient.yaml index 55ebc5d5..43cbfbbc 100644 --- a/packages/system/dashboard/templates/keycloakclient.yaml +++ b/packages/system/dashboard/templates/keycloakclient.yaml @@ -1,6 +1,13 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $extraRedirectUris := splitList "," ((index $cozyConfig.data "extra-keycloak-redirect-uri-for-dashboard") | default "") }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $authentication := $cozystack.authentication | default dict }} +{{- $oidc := $authentication.oidc | default dict }} +{{- $dashboard := $oidc.dashboard | default dict }} +{{- $host := $publishing.host | default "" }} +{{- $extraRedirectUris := $dashboard.extraRedirectUris | default list }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} {{- $existingK8sSecret := lookup "v1" "Secret" .Release.Namespace "k8s-client" }} {{- $existingDashboardSecret := lookup "v1" "Secret" .Release.Namespace "dashboard-client" }} @@ -70,7 +77,7 @@ spec: - kubernetes-client redirectUris: - "https://dashboard.{{ $host }}/oauth2/callback/*" - {{- range $i, $v := $extraRedirectUris }} - - "{{ $v }}" + {{- range $extraRedirectUris }} + - {{ . | quote }} {{- end }} {{- end }} diff --git a/packages/system/dashboard/templates/nginx-config.yaml b/packages/system/dashboard/templates/nginx-config.yaml index c2d6f624..de1aa74e 100644 --- a/packages/system/dashboard/templates/nginx-config.yaml +++ b/packages/system/dashboard/templates/nginx-config.yaml @@ -1,5 +1,9 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $host := $publishing.host | default "" }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} apiVersion: v1 data: diff --git a/packages/system/etcd-operator/Makefile b/packages/system/etcd-operator/Makefile index a2154952..54a1edad 100644 --- a/packages/system/etcd-operator/Makefile +++ b/packages/system/etcd-operator/Makefile @@ -1,7 +1,7 @@ export NAME=etcd-operator export NAMESPACE=cozy-${NAME} -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/external-dns/Makefile b/packages/system/external-dns/Makefile index 1ddfa773..5b0197e1 100644 --- a/packages/system/external-dns/Makefile +++ b/packages/system/external-dns/Makefile @@ -1,7 +1,7 @@ export NAME=external-dns export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/external-secrets-operator/Makefile b/packages/system/external-secrets-operator/Makefile index f4d9215d..0e1cd83a 100644 --- a/packages/system/external-secrets-operator/Makefile +++ b/packages/system/external-secrets-operator/Makefile @@ -1,7 +1,7 @@ export NAME=external-secrets-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/fluxcd-operator/Makefile b/packages/system/fluxcd-operator/Makefile index 84ffc6fe..432fb208 100644 --- a/packages/system/fluxcd-operator/Makefile +++ b/packages/system/fluxcd-operator/Makefile @@ -1,7 +1,7 @@ NAME=fluxcd-operator NAMESPACE=cozy-fluxcd -include ../../../scripts/package.mk +include ../../../hack/package.mk apply-locally: cozypkg apply --plain -n $(NAMESPACE) $(NAME) diff --git a/packages/system/fluxcd-operator/charts/flux-operator/Chart.yaml b/packages/system/fluxcd-operator/charts/flux-operator/Chart.yaml index b95a2a04..d59dedfa 100644 --- a/packages/system/fluxcd-operator/charts/flux-operator/Chart.yaml +++ b/packages/system/fluxcd-operator/charts/flux-operator/Chart.yaml @@ -8,7 +8,7 @@ annotations: - name: Upstream Project url: https://github.com/controlplaneio-fluxcd/flux-operator apiVersion: v2 -appVersion: v0.30.0 +appVersion: v0.33.0 description: 'A Helm chart for deploying the Flux Operator. ' home: https://github.com/controlplaneio-fluxcd icon: https://raw.githubusercontent.com/cncf/artwork/main/projects/flux/icon/color/flux-icon-color.png @@ -25,4 +25,4 @@ sources: - https://github.com/controlplaneio-fluxcd/flux-operator - https://github.com/controlplaneio-fluxcd/charts type: application -version: 0.30.0 +version: 0.33.0 diff --git a/packages/system/fluxcd-operator/charts/flux-operator/README.md b/packages/system/fluxcd-operator/charts/flux-operator/README.md index 0367d113..036fc534 100644 --- a/packages/system/fluxcd-operator/charts/flux-operator/README.md +++ b/packages/system/fluxcd-operator/charts/flux-operator/README.md @@ -1,6 +1,6 @@ # flux-operator -![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.30.0](https://img.shields.io/badge/AppVersion-v0.30.0-informational?style=flat-square) +![Version: 0.33.0](https://img.shields.io/badge/Version-0.33.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.33.0](https://img.shields.io/badge/AppVersion-v0.33.0-informational?style=flat-square) The [Flux Operator](https://github.com/controlplaneio-fluxcd/flux-operator) provides a declarative API for the installation and upgrade of CNCF [Flux](https://fluxcd.io) and the diff --git a/packages/system/fluxcd-operator/charts/flux-operator/templates/crds.yaml b/packages/system/fluxcd-operator/charts/flux-operator/templates/crds.yaml index 764382d2..fdd6fe76 100644 --- a/packages/system/fluxcd-operator/charts/flux-operator/templates/crds.yaml +++ b/packages/system/fluxcd-operator/charts/flux-operator/templates/crds.yaml @@ -203,6 +203,15 @@ spec: Registry address to pull the distribution images from e.g. 'ghcr.io/fluxcd'. type: string + variant: + description: |- + Variant specifies the Flux distribution flavor stored + in the registry. + enum: + - upstream-alpine + - enterprise-alpine + - enterprise-distroless + type: string version: description: Version semver expression e.g. '2.x', '2.3.x'. type: string @@ -583,6 +592,12 @@ spec: LastAttemptedRevision is the version and digest of the distribution config that was last attempted to reconcile. type: string + lastHandledForceAt: + description: |- + LastHandledForceAt holds the value of the most recent + force request value, so a change of the annotation value + can be detected. + type: string lastHandledReconcileAt: description: |- LastHandledReconcileAt holds the value of the most recent diff --git a/packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml b/packages/system/fluxcd-operator/packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml similarity index 93% rename from packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml rename to packages/system/fluxcd-operator/packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml index 0a9db1cc..5c7267ce 100644 --- a/packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml +++ b/packages/system/fluxcd-operator/packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml @@ -1,5 +1,4 @@ {{- if .Capabilities.APIVersions.Has "cilium.io/v2/CiliumClusterwideNetworkPolicy" }} ---- apiVersion: cilium.io/v2 kind: CiliumClusterwideNetworkPolicy metadata: @@ -17,5 +16,3 @@ spec: protocol: TCP ingress: - fromEntities: - - cluster -{{- end }} diff --git a/packages/system/fluxcd-operator/patches/kubernetesEnvs.diff b/packages/system/fluxcd-operator/patches/kubernetesEnvs.diff deleted file mode 100644 index 1c9c1c39..00000000 --- a/packages/system/fluxcd-operator/patches/kubernetesEnvs.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/packages/core/fluxcd/charts/flux-operator/templates/deployment.yaml b/packages/core/fluxcd/charts/flux-operator/templates/deployment.yaml -index 8ffd8d8..5ad96a8 100644 ---- a/charts/flux-operator/templates/deployment.yaml -+++ b/charts/flux-operator/templates/deployment.yaml -@@ -58,6 +58,7 @@ spec: - {{- if .Values.extraEnvs }} - {{- toYaml .Values.extraEnvs | nindent 12 }} - {{- end }} -+ {{- include "cozy.kubernetes_envs" . | nindent 12 }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" diff --git a/packages/system/fluxcd-operator/templates/_helpers.tpl b/packages/system/fluxcd-operator/templates/_helpers.tpl deleted file mode 100644 index e22979ba..00000000 --- a/packages/system/fluxcd-operator/templates/_helpers.tpl +++ /dev/null @@ -1,13 +0,0 @@ -{{- define "cozy.kubernetes_envs" }} -{{- $cozyDeployment := lookup "apps/v1" "Deployment" "cozy-system" "cozystack" }} -{{- $cozyContainers := dig "spec" "template" "spec" "containers" dict $cozyDeployment }} -{{- range $cozyContainers }} -{{- if eq .name "cozystack" }} -{{- range .env }} -{{- if has .name (list "KUBERNETES_SERVICE_HOST" "KUBERNETES_SERVICE_PORT") }} -- {{ toJson . }} -{{- end }} -{{- end }} -{{- end }} -{{- end }} -{{- end }} diff --git a/packages/system/fluxcd-operator/values.yaml b/packages/system/fluxcd-operator/values.yaml index 9053689a..81bc1924 100644 --- a/packages/system/fluxcd-operator/values.yaml +++ b/packages/system/fluxcd-operator/values.yaml @@ -4,7 +4,6 @@ flux-operator: - key: node.kubernetes.io/not-ready operator: Exists effect: NoSchedule - hostNetwork: true resources: limits: cpu: 100m diff --git a/packages/system/fluxcd/Makefile b/packages/system/fluxcd/Makefile index b5af8680..46beb480 100644 --- a/packages/system/fluxcd/Makefile +++ b/packages/system/fluxcd/Makefile @@ -1,7 +1,7 @@ NAME=fluxcd NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk apply-locally: cozypkg apply --plain -n $(NAMESPACE) $(NAME) diff --git a/packages/system/fluxcd/charts/flux-instance/Chart.yaml b/packages/system/fluxcd/charts/flux-instance/Chart.yaml index 90dfadb5..448e6600 100644 --- a/packages/system/fluxcd/charts/flux-instance/Chart.yaml +++ b/packages/system/fluxcd/charts/flux-instance/Chart.yaml @@ -8,7 +8,7 @@ annotations: - name: Upstream Project url: https://github.com/controlplaneio-fluxcd/flux-operator apiVersion: v2 -appVersion: v0.30.0 +appVersion: v0.33.0 description: 'A Helm chart for deploying a Flux instance managed by Flux Operator. ' home: https://github.com/controlplaneio-fluxcd icon: https://raw.githubusercontent.com/cncf/artwork/main/projects/flux/icon/color/flux-icon-color.png @@ -25,4 +25,4 @@ sources: - https://github.com/controlplaneio-fluxcd/flux-operator - https://github.com/controlplaneio-fluxcd/charts type: application -version: 0.30.0 +version: 0.33.0 diff --git a/packages/system/fluxcd/charts/flux-instance/README.md b/packages/system/fluxcd/charts/flux-instance/README.md index a6611caf..e788edf1 100644 --- a/packages/system/fluxcd/charts/flux-instance/README.md +++ b/packages/system/fluxcd/charts/flux-instance/README.md @@ -1,6 +1,6 @@ # flux-instance -![Version: 0.30.0](https://img.shields.io/badge/Version-0.30.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.30.0](https://img.shields.io/badge/AppVersion-v0.30.0-informational?style=flat-square) +![Version: 0.33.0](https://img.shields.io/badge/Version-0.33.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.33.0](https://img.shields.io/badge/AppVersion-v0.33.0-informational?style=flat-square) This chart is a thin wrapper around the `FluxInstance` custom resource, which is used by the [Flux Operator](https://github.com/controlplaneio-fluxcd/flux-operator) diff --git a/packages/system/fluxcd/values.yaml b/packages/system/fluxcd/values.yaml index 837be8d8..e269b1fb 100644 --- a/packages/system/fluxcd/values.yaml +++ b/packages/system/fluxcd/values.yaml @@ -1,14 +1,15 @@ flux-instance: instance: cluster: - networkPolicy: true + networkPolicy: false # -- disable due to liveness/readiness probes issues with network policies domain: cozy.local # -- default value is overriden in patches distribution: artifact: "" - version: 2.6.x + version: 2.7.x registry: ghcr.io/fluxcd components: - source-controller + - source-watcher - kustomize-controller - helm-controller - notification-controller @@ -38,12 +39,16 @@ flux-instance: - op: add path: /spec/template/spec/containers/0/args/- value: --storage-adv-addr=source-controller.cozy-fluxcd.svc - - op: add - path: /spec/template/spec/containers/0/args/- - value: --events-addr=http://notification-controller.cozy-fluxcd.svc/ - target: kind: Deployment - name: (kustomize-controller|helm-controller|image-reflector-controller|image-automation-controller) + name: source-watcher + patch: | + - op: add + path: /spec/template/spec/containers/0/args/- + value: --storage-adv-addr=source-watcher.cozy-fluxcd.svc + - target: + kind: Deployment + name: (kustomize-controller|helm-controller|image-reflector-controller|image-automation-controller|source-controller|source-watcher) patch: | - op: add path: /spec/template/spec/containers/0/args/- diff --git a/packages/system/foundationdb-operator/Makefile b/packages/system/foundationdb-operator/Makefile index f735e7c5..16ca953f 100644 --- a/packages/system/foundationdb-operator/Makefile +++ b/packages/system/foundationdb-operator/Makefile @@ -1,7 +1,7 @@ export NAME=foundationdb-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/gateway-api-crds/Makefile b/packages/system/gateway-api-crds/Makefile index c4311662..ad51d591 100644 --- a/packages/system/gateway-api-crds/Makefile +++ b/packages/system/gateway-api-crds/Makefile @@ -1,7 +1,7 @@ export NAME=gateway-api-crds export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/goldpinger/Makefile b/packages/system/goldpinger/Makefile index 3ddd79ba..0aeef2ad 100644 --- a/packages/system/goldpinger/Makefile +++ b/packages/system/goldpinger/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/gpu-operator/Makefile b/packages/system/gpu-operator/Makefile index 286451f3..49b26e26 100644 --- a/packages/system/gpu-operator/Makefile +++ b/packages/system/gpu-operator/Makefile @@ -1,8 +1,8 @@ export NAME=gpu-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/grafana-operator/Makefile b/packages/system/grafana-operator/Makefile index 82c734d4..b1e442f4 100644 --- a/packages/system/grafana-operator/Makefile +++ b/packages/system/grafana-operator/Makefile @@ -1,10 +1,22 @@ export NAME=grafana-operator export NAMESPACE=cozy-grafana-operator -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts mkdir -p charts curl -sSL https://github.com/grafana-operator/grafana-operator/archive/refs/heads/master.tar.gz | \ tar xzvf - --strip 3 -C charts grafana-operator-master/deploy/helm/grafana-operator + +image: + docker buildx build -f images/grafana-dashboards/Dockerfile ../../.. \ + --tag $(REGISTRY)/grafana-dashboards:$(call settag,$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/grafana-dashboards:latest \ + --cache-to type=inline \ + --metadata-file images/grafana-dashboards.json \ + $(BUILDX_ARGS) + echo "$(REGISTRY)/grafana-dashboards:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/grafana-dashboards.json -o json -r)" \ + > images/grafana-dashboards.tag + rm -f images/grafana-dashboards.json diff --git a/packages/system/grafana-operator/images/grafana-dashboards.tag b/packages/system/grafana-operator/images/grafana-dashboards.tag new file mode 100644 index 00000000..c0277bfe --- /dev/null +++ b/packages/system/grafana-operator/images/grafana-dashboards.tag @@ -0,0 +1 @@ +ghcr.io/cozystack/cozystack/grafana-dashboards:v0.38.0-alpha.1@sha256:7e81580beb417ef8a54492b35b2b78cd51ed991f7dca65be6d771ca4ca1f1d61 diff --git a/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile b/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile new file mode 100644 index 00000000..619f09fa --- /dev/null +++ b/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:3.22 + +RUN apk add --no-cache darkhttpd + +COPY dashboards /var/www/dashboards + +WORKDIR /var/www + +EXPOSE 8080 + +CMD ["darkhttpd", "/var/www/dashboards", "--port", "8080", "--addr", "0.0.0.0"] + diff --git a/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile.dockerignore b/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile.dockerignore new file mode 100644 index 00000000..fa588871 --- /dev/null +++ b/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile.dockerignore @@ -0,0 +1,3 @@ +# Exclude everything except dashboards directory +* +!dashboards/** diff --git a/packages/system/grafana-operator/templates/dashboards-deployment.yaml b/packages/system/grafana-operator/templates/dashboards-deployment.yaml new file mode 100644 index 00000000..e1e81b45 --- /dev/null +++ b/packages/system/grafana-operator/templates/dashboards-deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grafana-dashboards + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: dashboards +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: dashboards + spec: + containers: + - name: dashboards + image: {{ $.Files.Get "images/grafana-dashboards.tag" | trim }} + ports: + - containerPort: 8080 + name: http + protocol: TCP + livenessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + diff --git a/packages/system/grafana-operator/templates/dashboards-service.yaml b/packages/system/grafana-operator/templates/dashboards-service.yaml new file mode 100644 index 00000000..e4b6859e --- /dev/null +++ b/packages/system/grafana-operator/templates/dashboards-service.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: grafana-dashboards + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: dashboards +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + diff --git a/packages/system/hetzner-robotlb/Makefile b/packages/system/hetzner-robotlb/Makefile index 9cd69463..c5ea0e4d 100644 --- a/packages/system/hetzner-robotlb/Makefile +++ b/packages/system/hetzner-robotlb/Makefile @@ -1,7 +1,7 @@ export NAME=hetzner-robotlb export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/ingress-nginx/Makefile b/packages/system/ingress-nginx/Makefile index 9ad10ae1..573c253b 100644 --- a/packages/system/ingress-nginx/Makefile +++ b/packages/system/ingress-nginx/Makefile @@ -1,6 +1,6 @@ NAME=ingress-nginx-system -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/kafka-operator/Makefile b/packages/system/kafka-operator/Makefile index 32fa2207..0e4811e5 100644 --- a/packages/system/kafka-operator/Makefile +++ b/packages/system/kafka-operator/Makefile @@ -1,7 +1,7 @@ export NAME=kafka-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/kamaji/Makefile b/packages/system/kamaji/Makefile index c24e2d4e..f82c4c7a 100644 --- a/packages/system/kamaji/Makefile +++ b/packages/system/kamaji/Makefile @@ -1,8 +1,8 @@ export NAME=kamaji export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/keycloak-configure/Makefile b/packages/system/keycloak-configure/Makefile index b9fd5c10..4cd5bd2e 100644 --- a/packages/system/keycloak-configure/Makefile +++ b/packages/system/keycloak-configure/Makefile @@ -1,5 +1,5 @@ export NAME=keycloak-configure export NAMESPACE=cozy-keycloak -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk diff --git a/packages/system/keycloak-configure/templates/configure-kk.yaml b/packages/system/keycloak-configure/templates/configure-kk.yaml index 3fc92c86..11dc808a 100644 --- a/packages/system/keycloak-configure/templates/configure-kk.yaml +++ b/packages/system/keycloak-configure/templates/configure-kk.yaml @@ -1,13 +1,18 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $extraRedirectUris := splitList "," ((index $cozyConfig.data "extra-keycloak-redirect-uri-for-dashboard") | default "") }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $authentication := $cozystack.authentication | default dict }} +{{- $oidc := $authentication.oidc | default dict }} +{{- $branding := $cozystack.branding | default dict }} +{{- $host := $publishing.host | default "" }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} {{- $rootSaConfigMap := lookup "v1" "ConfigMap" "kube-system" "kube-root-ca.crt" }} {{- $k8sCa := index $rootSaConfigMap.data "ca.crt" | b64enc }} {{- $existingK8sSecret := lookup "v1" "Secret" .Release.Namespace "k8s-client" }} {{- $existingKubeappsSecret := lookup "v1" "Secret" .Release.Namespace "kubeapps-client" }} {{- $existingAuthConfig := lookup "v1" "Secret" "cozy-dashboard" "kubeapps-auth-config" }} -{{- $cozystackBranding:= lookup "v1" "ConfigMap" "cozy-system" "cozystack-branding" }} {{ $k8sClient := "" }} {{- if $existingK8sSecret }} @@ -16,10 +21,8 @@ {{- $k8sClient = randAlphaNum 32 }} {{- end }} -{{ $branding := "" }} -{{- if $cozystackBranding }} - {{- $branding = index $cozystackBranding.data "branding" }} -{{- end }} +{{ $displayName := dig "displayName" "" $branding }} +{{ $displayHtmlName := dig "displayHtmlName" "" $branding }} --- @@ -53,9 +56,11 @@ metadata: spec: realmName: cozy clusterKeycloakRef: keycloak-cozy - {{- if $branding }} - displayHtmlName: {{ $branding }} - displayName: {{ $branding }} + {{- if $displayHtmlName }} + displayHtmlName: {{ $displayHtmlName }} + {{- end }} + {{- if $displayName }} + displayName: {{ $displayName }} {{- end }} --- diff --git a/packages/system/keycloak-operator/Makefile b/packages/system/keycloak-operator/Makefile index a4f50b67..9c8d63c1 100644 --- a/packages/system/keycloak-operator/Makefile +++ b/packages/system/keycloak-operator/Makefile @@ -1,8 +1,8 @@ export NAME=keycloak-operator export NAMESPACE=cozy-keycloak -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/keycloak/Makefile b/packages/system/keycloak/Makefile index 62f4e98d..09afc864 100644 --- a/packages/system/keycloak/Makefile +++ b/packages/system/keycloak/Makefile @@ -1,5 +1,5 @@ export NAME=keycloak export NAMESPACE=cozy-keycloak -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk diff --git a/packages/system/keycloak/templates/db.yaml b/packages/system/keycloak/templates/db.yaml index 70666d7b..fc0c9832 100644 --- a/packages/system/keycloak/templates/db.yaml +++ b/packages/system/keycloak/templates/db.yaml @@ -6,16 +6,18 @@ spec: instances: 2 storage: size: 20Gi - {{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} - {{- if $configMap }} - {{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }} - {{- if $rawConstraints }} - {{- $rawConstraints | fromYaml | toYaml | nindent 2 }} + {{- $cozystack := .Values.cozystack | default dict }} + {{- $scheduling := $cozystack.scheduling | default dict }} + {{- $topologySpreadConstraints := $scheduling.topologySpreadConstraints | default list }} + {{- if $topologySpreadConstraints }} + topologySpreadConstraints: + {{- range $topologySpreadConstraints }} + - {{ . | toYaml | nindent 6 | trim }} + {{- end }} labelSelector: matchLabels: cnpg.io/cluster: keycloak-db {{- end }} - {{- end }} monitoring: enablePodMonitor: true diff --git a/packages/system/keycloak/templates/ingress.yaml b/packages/system/keycloak/templates/ingress.yaml index 30120619..95cb4270 100644 --- a/packages/system/keycloak/templates/ingress.yaml +++ b/packages/system/keycloak/templates/ingress.yaml @@ -1,7 +1,12 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $issuerType := (index $cozyConfig.data "clusterissuer") | default "http01" }} -{{- $exposeIngress := index $cozyConfig.data "expose-ingress" | default "tenant-root" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $certificates := $publishing.certificates | default dict }} +{{- $host := $publishing.host | default "" }} +{{- $issuerType := $certificates.issuerType | default "http01" }} +{{- $exposeIngress := $publishing.ingressClassName | default "tenant-root" }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} apiVersion: networking.k8s.io/v1 kind: Ingress diff --git a/packages/system/keycloak/templates/sts.yaml b/packages/system/keycloak/templates/sts.yaml index 4705f77c..fa8a2cc8 100644 --- a/packages/system/keycloak/templates/sts.yaml +++ b/packages/system/keycloak/templates/sts.yaml @@ -1,6 +1,11 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $networking := $cozystack.networking | default dict }} +{{- $host := $publishing.host | default "" }} +{{- $clusterDomain := $networking.clusterDomain | default "cozy.local" }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} {{- $existingPassword := lookup "v1" "Secret" "cozy-keycloak" (printf "%s-credentials" .Release.Name) }} {{- $password := randAlphaNum 16 -}} diff --git a/packages/system/kubeovn-plunger/Makefile b/packages/system/kubeovn-plunger/Makefile index a80c7e06..f56d935b 100644 --- a/packages/system/kubeovn-plunger/Makefile +++ b/packages/system/kubeovn-plunger/Makefile @@ -1,8 +1,8 @@ export NAME=kubeovn-plunger export NAMESPACE=cozy-kubeovn -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: docker buildx build -f images/kubeovn-plunger/Dockerfile ../../../ \ diff --git a/packages/system/kubeovn-plunger/images/kubeovn-plunger/Dockerfile b/packages/system/kubeovn-plunger/images/kubeovn-plunger/Dockerfile index a6264ae1..b0473244 100644 --- a/packages/system/kubeovn-plunger/images/kubeovn-plunger/Dockerfile +++ b/packages/system/kubeovn-plunger/images/kubeovn-plunger/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24-alpine AS builder +FROM golang:1.25-alpine AS builder ARG TARGETOS ARG TARGETARCH diff --git a/packages/system/kubeovn-webhook/Makefile b/packages/system/kubeovn-webhook/Makefile index 9134d55f..9b2e2744 100644 --- a/packages/system/kubeovn-webhook/Makefile +++ b/packages/system/kubeovn-webhook/Makefile @@ -1,8 +1,8 @@ export NAME=kubeovn-webhook export NAMESPACE=cozy-kubeovn -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: docker buildx build images/kubeovn-webhook \ diff --git a/packages/system/kubeovn/Makefile b/packages/system/kubeovn/Makefile index 15373d2f..988a5f5c 100644 --- a/packages/system/kubeovn/Makefile +++ b/packages/system/kubeovn/Makefile @@ -3,15 +3,14 @@ KUBEOVN_TAG=$(shell awk '$$1 == "version:" {print $$2}' charts/kube-ovn/Chart.ya export NAME=kubeovn export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts && mkdir -p charts/kube-ovn tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/kubeovn/kube-ovn | awk -F'[/^]' '{print $$3}' | grep '^v1\.14\.' | tail -n1 ) && \ curl -sSL https://github.com/kubeovn/kube-ovn/archive/refs/tags/$${tag}.tar.gz | \ tar xzvf - --strip 1 kube-ovn-$${tag#*v}/charts/kube-ovn - patch --no-backup-if-mismatch -p4 < patches/cozyconfig.diff patch --no-backup-if-mismatch -p4 < patches/mtu.diff version=$$(awk '$$1 == "version:" {print $$2}' charts/kube-ovn/Chart.yaml) && \ sed -i "s/ARG VERSION=.*/ARG VERSION=$${version}/" images/kubeovn/Dockerfile && \ diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml index 5c4587f9..3675dffc 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml @@ -70,21 +70,45 @@ spec: image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} imagePullPolicy: {{ .Values.image.pullPolicy }} args: - {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} - /kube-ovn/start-controller.sh - --default-ls={{ .Values.networking.DEFAULT_SUBNET }} - - --default-cidr={{ index $cozyConfig.data "ipv4-pod-cidr" }} - - --default-gateway={{ index $cozyConfig.data "ipv4-pod-gateway" }} + - --default-cidr= + {{- if eq .Values.networking.NET_STACK "dual_stack" -}} + {{ .Values.dual_stack.POD_CIDR }} + {{- else if eq .Values.networking.NET_STACK "ipv4" -}} + {{ .Values.ipv4.POD_CIDR }} + {{- else if eq .Values.networking.NET_STACK "ipv6" -}} + {{ .Values.ipv6.POD_CIDR }} + {{- end }} + - --default-gateway= + {{- if eq .Values.networking.NET_STACK "dual_stack" -}} + {{ .Values.dual_stack.POD_GATEWAY }} + {{- else if eq .Values.networking.NET_STACK "ipv4" -}} + {{ .Values.ipv4.POD_GATEWAY }} + {{- else if eq .Values.networking.NET_STACK "ipv6" -}} + {{ .Values.ipv6.POD_GATEWAY }} + {{- end }} - --default-gateway-check={{- .Values.func.CHECK_GATEWAY }} - --default-logical-gateway={{- .Values.func.LOGICAL_GATEWAY }} - --default-u2o-interconnection={{- .Values.func.U2O_INTERCONNECTION }} - --default-exclude-ips={{- .Values.networking.EXCLUDE_IPS }} - --cluster-router={{ .Values.networking.DEFAULT_VPC }} - --node-switch={{ .Values.networking.NODE_SUBNET }} - - --node-switch-cidr={{ index $cozyConfig.data "ipv4-join-cidr" }} - - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }} - {{- if .Values.global.logVerbosity }} - - --v={{ .Values.global.logVerbosity }} + - --node-switch-cidr= + {{- if eq .Values.networking.NET_STACK "dual_stack" -}} + {{ .Values.dual_stack.JOIN_CIDR }} + {{- else if eq .Values.networking.NET_STACK "ipv4" -}} + {{ .Values.ipv4.JOIN_CIDR }} + {{- else if eq .Values.networking.NET_STACK "ipv6" -}} + {{ .Values.ipv6.JOIN_CIDR }} + {{- end }} + - --service-cluster-ip-range= + {{- if eq .Values.networking.NET_STACK "dual_stack" -}} + {{ .Values.dual_stack.SVC_CIDR }} + {{- else if eq .Values.networking.NET_STACK "ipv4" -}} + {{ .Values.ipv4.SVC_CIDR }} + {{- else if eq .Values.networking.NET_STACK "ipv6" -}} + {{ .Values.ipv6.SVC_CIDR }} {{- end }} - --network-type={{- .Values.networking.NETWORK_TYPE }} - --default-provider-name={{ .Values.networking.vlan.PROVIDER_NAME }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml index 947ec454..98ebfeab 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml @@ -83,14 +83,17 @@ spec: - bash - /kube-ovn/start-cniserver.sh args: - {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} - --enable-mirror={{- .Values.debug.ENABLE_MIRROR }} - --mirror-iface={{- .Values.debug.MIRROR_IFACE }} - --node-switch={{ .Values.networking.NODE_SUBNET }} - --encap-checksum=true - - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }} - {{- if .Values.global.logVerbosity }} - - --v={{ .Values.global.logVerbosity }} + - --service-cluster-ip-range= + {{- if eq .Values.networking.NET_STACK "dual_stack" -}} + {{ .Values.dual_stack.SVC_CIDR }} + {{- else if eq .Values.networking.NET_STACK "ipv4" -}} + {{ .Values.ipv4.SVC_CIDR }} + {{- else if eq .Values.networking.NET_STACK "ipv6" -}} + {{ .Values.ipv6.SVC_CIDR }} {{- end }} {{- if eq .Values.networking.NETWORK_TYPE "vlan" }} - --iface= diff --git a/packages/system/kubeovn/charts/kube-ovn/values.yaml b/packages/system/kubeovn/charts/kube-ovn/values.yaml index 3652386d..1799c3f3 100644 --- a/packages/system/kubeovn/charts/kube-ovn/values.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/values.yaml @@ -80,6 +80,10 @@ func: ENABLE_OVN_LB_PREFER_LOCAL: false ipv4: + POD_CIDR: "10.16.0.0/16" + POD_GATEWAY: "10.16.0.1" + SVC_CIDR: "10.96.0.0/12" + JOIN_CIDR: "100.64.0.0/16" PINGER_EXTERNAL_ADDRESS: "1.1.1.1" PINGER_EXTERNAL_DOMAIN: "kube-ovn.io." diff --git a/packages/system/kubeovn/patches/cozyconfig.diff b/packages/system/kubeovn/patches/cozyconfig.diff deleted file mode 100644 index f7a683f7..00000000 --- a/packages/system/kubeovn/patches/cozyconfig.diff +++ /dev/null @@ -1,103 +0,0 @@ - -diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml -index d9a9a67..b2e12dd 100644 ---- a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml -+++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml -@@ -51,18 +51,15 @@ spec: - - bash - - /kube-ovn/start-cniserver.sh - args: -+ {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} - - --enable-mirror={{- .Values.debug.ENABLE_MIRROR }} - - --mirror-iface={{- .Values.debug.MIRROR_IFACE }} - - --node-switch={{ .Values.networking.NODE_SUBNET }} - - --encap-checksum=true -- - --service-cluster-ip-range= -- {{- if eq .Values.networking.NET_STACK "dual_stack" -}} -- {{ .Values.dual_stack.SVC_CIDR }} -- {{- else if eq .Values.networking.NET_STACK "ipv4" -}} -- {{ .Values.ipv4.SVC_CIDR }} -- {{- else if eq .Values.networking.NET_STACK "ipv6" -}} -- {{ .Values.ipv6.SVC_CIDR }} -- {{- end }} -+ - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }} -+ {{- if .Values.global.logVerbosity }} -+ - --v={{ .Values.global.logVerbosity }} -+ {{- end }} - {{- if eq .Values.networking.NETWORK_TYPE "vlan" }} - - --iface= - {{- else}} -diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml -index 0e69494..756eb7c 100644 ---- a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml -+++ b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml -@@ -52,46 +52,22 @@ spec: - image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - args: -+ {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} - - /kube-ovn/start-controller.sh - - --default-ls={{ .Values.networking.DEFAULT_SUBNET }} -- - --default-cidr= -- {{- if eq .Values.networking.NET_STACK "dual_stack" -}} -- {{ .Values.dual_stack.POD_CIDR }} -- {{- else if eq .Values.networking.NET_STACK "ipv4" -}} -- {{ .Values.ipv4.POD_CIDR }} -- {{- else if eq .Values.networking.NET_STACK "ipv6" -}} -- {{ .Values.ipv6.POD_CIDR }} -- {{- end }} -- - --default-gateway= -- {{- if eq .Values.networking.NET_STACK "dual_stack" -}} -- {{ .Values.dual_stack.POD_GATEWAY }} -- {{- else if eq .Values.networking.NET_STACK "ipv4" -}} -- {{ .Values.ipv4.POD_GATEWAY }} -- {{- else if eq .Values.networking.NET_STACK "ipv6" -}} -- {{ .Values.ipv6.POD_GATEWAY }} -- {{- end }} -+ - --default-cidr={{ index $cozyConfig.data "ipv4-pod-cidr" }} -+ - --default-gateway={{ index $cozyConfig.data "ipv4-pod-gateway" }} - - --default-gateway-check={{- .Values.func.CHECK_GATEWAY }} - - --default-logical-gateway={{- .Values.func.LOGICAL_GATEWAY }} - - --default-u2o-interconnection={{- .Values.func.U2O_INTERCONNECTION }} - - --default-exclude-ips={{- .Values.networking.EXCLUDE_IPS }} - - --cluster-router={{ .Values.networking.DEFAULT_VPC }} - - --node-switch={{ .Values.networking.NODE_SUBNET }} -- - --node-switch-cidr= -- {{- if eq .Values.networking.NET_STACK "dual_stack" -}} -- {{ .Values.dual_stack.JOIN_CIDR }} -- {{- else if eq .Values.networking.NET_STACK "ipv4" -}} -- {{ .Values.ipv4.JOIN_CIDR }} -- {{- else if eq .Values.networking.NET_STACK "ipv6" -}} -- {{ .Values.ipv6.JOIN_CIDR }} -- {{- end }} -- - --service-cluster-ip-range= -- {{- if eq .Values.networking.NET_STACK "dual_stack" -}} -- {{ .Values.dual_stack.SVC_CIDR }} -- {{- else if eq .Values.networking.NET_STACK "ipv4" -}} -- {{ .Values.ipv4.SVC_CIDR }} -- {{- else if eq .Values.networking.NET_STACK "ipv6" -}} -- {{ .Values.ipv6.SVC_CIDR }} -- {{- end }} -+ - --node-switch-cidr={{ index $cozyConfig.data "ipv4-join-cidr" }} -+ - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }} -+ {{- if .Values.global.logVerbosity }} -+ - --v={{ .Values.global.logVerbosity }} -+ {{- end }} - - --network-type={{- .Values.networking.NETWORK_TYPE }} - - --default-provider-name={{ .Values.networking.vlan.PROVIDER_NAME }} - - --default-interface-name={{- .Values.networking.vlan.VLAN_INTERFACE_NAME }} -diff --git a/packages/system/kubeovn/charts/kube-ovn/values.yaml b/packages/system/kubeovn/charts/kube-ovn/values.yaml -index bfffc4d..b880749 100644 ---- a/packages/system/kubeovn/charts/kube-ovn/values.yaml -+++ b/packages/system/kubeovn/charts/kube-ovn/values.yaml -@@ -70,10 +70,6 @@ func: - ENABLE_TPROXY: false - - ipv4: -- POD_CIDR: "10.16.0.0/16" -- POD_GATEWAY: "10.16.0.1" -- SVC_CIDR: "10.96.0.0/12" -- JOIN_CIDR: "100.64.0.0/16" - PINGER_EXTERNAL_ADDRESS: "1.1.1.1" - PINGER_EXTERNAL_DOMAIN: "alauda.cn." - diff --git a/packages/system/kubevirt-cdi-operator/Makefile b/packages/system/kubevirt-cdi-operator/Makefile index 7022599f..7232216f 100644 --- a/packages/system/kubevirt-cdi-operator/Makefile +++ b/packages/system/kubevirt-cdi-operator/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt-cdi-operator export NAMESPACE=cozy-kubevirt-cdi -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt-cdi/Makefile b/packages/system/kubevirt-cdi/Makefile index 0b3791a1..709e4880 100644 --- a/packages/system/kubevirt-cdi/Makefile +++ b/packages/system/kubevirt-cdi/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt-cdi export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt-cdi/templates/cdi-cr.yaml b/packages/system/kubevirt-cdi/templates/cdi-cr.yaml index 8f57ec43..69c8056e 100644 --- a/packages/system/kubevirt-cdi/templates/cdi-cr.yaml +++ b/packages/system/kubevirt-cdi/templates/cdi-cr.yaml @@ -35,7 +35,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: cdi-clone-dv - namespace: cozy-public + namespace: cozy-system subjects: - kind: Group name: system:serviceaccounts diff --git a/packages/system/kubevirt-cdi/templates/cdi-uploadproxy-ingress.yaml b/packages/system/kubevirt-cdi/templates/cdi-uploadproxy-ingress.yaml index 58eef4fa..081368c8 100644 --- a/packages/system/kubevirt-cdi/templates/cdi-uploadproxy-ingress.yaml +++ b/packages/system/kubevirt-cdi/templates/cdi-uploadproxy-ingress.yaml @@ -1,10 +1,13 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $exposeServices := splitList "," ((index $cozyConfig.data "expose-services") | default "") }} -{{- $exposeIngress := index $cozyConfig.data "expose-ingress" | default "tenant-root" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $host := $publishing.host | default "" }} +{{- $exposeServices := $publishing.exposedServices | default list }} +{{- $exposeIngress := $publishing.ingressClassName | default "tenant-root" }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} - -{{- if and (has "cdi-uploadproxy" $exposeServices) }} +{{- if has "cdi-uploadproxy" $exposeServices }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/packages/system/kubevirt-instancetypes/Makefile b/packages/system/kubevirt-instancetypes/Makefile index d0498f10..b9f84b83 100644 --- a/packages/system/kubevirt-instancetypes/Makefile +++ b/packages/system/kubevirt-instancetypes/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt-instancetypes export NAMESPACE=cozy-kubevirt -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt-operator/Makefile b/packages/system/kubevirt-operator/Makefile index 42bf80a7..69b6a692 100644 --- a/packages/system/kubevirt-operator/Makefile +++ b/packages/system/kubevirt-operator/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt-operator export NAMESPACE=cozy-kubevirt -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt/Makefile b/packages/system/kubevirt/Makefile index 2c2e57ab..246001d0 100644 --- a/packages/system/kubevirt/Makefile +++ b/packages/system/kubevirt/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt/templates/vm-exportproxy-ingress.yaml b/packages/system/kubevirt/templates/vm-exportproxy-ingress.yaml index b77743d0..4e2bbd46 100644 --- a/packages/system/kubevirt/templates/vm-exportproxy-ingress.yaml +++ b/packages/system/kubevirt/templates/vm-exportproxy-ingress.yaml @@ -1,9 +1,13 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- $exposeServices := splitList "," ((index $cozyConfig.data "expose-services") | default "") }} -{{- $exposeIngress := index $cozyConfig.data "expose-ingress" | default "tenant-root" }} +{{- $cozystack := .Values.cozystack | default dict }} +{{- $publishing := $cozystack.publishing | default dict }} +{{- $host := $publishing.host | default "" }} +{{- $exposeServices := $publishing.exposedServices | default list }} +{{- $exposeIngress := $publishing.ingressClassName | default "tenant-root" }} +{{- if not $host }} +{{- fail "ERROR: cozystack.publishing.host is required" }} +{{- end }} -{{- if and (has "vm-exportproxy" $exposeServices) }} +{{- if has "vm-exportproxy" $exposeServices }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: diff --git a/packages/system/lineage-controller-webhook/Makefile b/packages/system/lineage-controller-webhook/Makefile index 04d81a12..d5bada31 100644 --- a/packages/system/lineage-controller-webhook/Makefile +++ b/packages/system/lineage-controller-webhook/Makefile @@ -1,8 +1,8 @@ NAME=lineage-controller-webhook NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: image-lineage-controller-webhook diff --git a/packages/system/lineage-controller-webhook/images/lineage-controller-webhook/Dockerfile b/packages/system/lineage-controller-webhook/images/lineage-controller-webhook/Dockerfile index e043e472..d9232d98 100644 --- a/packages/system/lineage-controller-webhook/images/lineage-controller-webhook/Dockerfile +++ b/packages/system/lineage-controller-webhook/images/lineage-controller-webhook/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24-alpine AS builder +FROM golang:1.25-alpine AS builder ARG TARGETOS ARG TARGETARCH diff --git a/packages/system/linstor/Makefile b/packages/system/linstor/Makefile index 5de22194..f0829ec0 100644 --- a/packages/system/linstor/Makefile +++ b/packages/system/linstor/Makefile @@ -1,4 +1,4 @@ export NAME=linstor export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/mariadb-operator/Makefile b/packages/system/mariadb-operator/Makefile index ecbd51d9..905653ca 100644 --- a/packages/system/mariadb-operator/Makefile +++ b/packages/system/mariadb-operator/Makefile @@ -1,7 +1,7 @@ export NAME=mariadb-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/metallb/Makefile b/packages/system/metallb/Makefile index 5323f414..5262e558 100644 --- a/packages/system/metallb/Makefile +++ b/packages/system/metallb/Makefile @@ -1,8 +1,8 @@ export NAME=metallb export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/monitoring-agents/Makefile b/packages/system/monitoring-agents/Makefile index 42e762c3..c8b58c7f 100644 --- a/packages/system/monitoring-agents/Makefile +++ b/packages/system/monitoring-agents/Makefile @@ -1,7 +1,7 @@ export NAME=monitoring-agents export NAMESPACE=cozy-monitoring -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/multus/Makefile b/packages/system/multus/Makefile index 7825bd5e..9261404d 100644 --- a/packages/system/multus/Makefile +++ b/packages/system/multus/Makefile @@ -1,8 +1,8 @@ export NAME=multus export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/nats/Makefile b/packages/system/nats/Makefile index 25657a89..88883ab0 100644 --- a/packages/system/nats/Makefile +++ b/packages/system/nats/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/nfs-driver/Makefile b/packages/system/nfs-driver/Makefile index e3af6c03..2f14aa28 100644 --- a/packages/system/nfs-driver/Makefile +++ b/packages/system/nfs-driver/Makefile @@ -1,8 +1,8 @@ export NAME=nfs-driver export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/objectstorage-controller/Makefile b/packages/system/objectstorage-controller/Makefile index 54c195a0..dacc3353 100644 --- a/packages/system/objectstorage-controller/Makefile +++ b/packages/system/objectstorage-controller/Makefile @@ -1,8 +1,8 @@ export NAME=objectstorage-controller export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/piraeus-operator/Makefile b/packages/system/piraeus-operator/Makefile index a10c7369..b46680f2 100644 --- a/packages/system/piraeus-operator/Makefile +++ b/packages/system/piraeus-operator/Makefile @@ -1,7 +1,7 @@ export NAME=piraeus-operator export NAMESPACE=cozy-linstor -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/postgres-operator/Makefile b/packages/system/postgres-operator/Makefile index f1a0bba0..f58f235f 100644 --- a/packages/system/postgres-operator/Makefile +++ b/packages/system/postgres-operator/Makefile @@ -1,7 +1,7 @@ export NAME=postgres-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/rabbitmq-operator/Makefile b/packages/system/rabbitmq-operator/Makefile index 12eda697..a75e37cd 100644 --- a/packages/system/rabbitmq-operator/Makefile +++ b/packages/system/rabbitmq-operator/Makefile @@ -1,7 +1,7 @@ export NAME=rabbitmq-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates/cluster-operator.yml diff --git a/packages/system/redis-operator/Makefile b/packages/system/redis-operator/Makefile index 650cbc72..9e21b1b4 100644 --- a/packages/system/redis-operator/Makefile +++ b/packages/system/redis-operator/Makefile @@ -2,8 +2,8 @@ REDIS_OPERATOR_TAG=$(shell grep -F 'ARG VERSION=' images/redis-operator/Dockerfi export NAME=redis-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/reloader/Makefile b/packages/system/reloader/Makefile index 378dc23c..6dc1f004 100644 --- a/packages/system/reloader/Makefile +++ b/packages/system/reloader/Makefile @@ -1,7 +1,7 @@ export NAME=reloader export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/seaweedfs/Makefile b/packages/system/seaweedfs/Makefile index 2344f6b5..2f237cef 100644 --- a/packages/system/seaweedfs/Makefile +++ b/packages/system/seaweedfs/Makefile @@ -1,7 +1,7 @@ export NAME=seaweedfs-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/snapshot-controller/Makefile b/packages/system/snapshot-controller/Makefile index 65d37f1f..ca5d39e7 100644 --- a/packages/system/snapshot-controller/Makefile +++ b/packages/system/snapshot-controller/Makefile @@ -1,7 +1,7 @@ export NAME=snapshot-controller export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/telepresence/Makefile b/packages/system/telepresence/Makefile index 1aec8917..d7f83e05 100644 --- a/packages/system/telepresence/Makefile +++ b/packages/system/telepresence/Makefile @@ -1,7 +1,7 @@ export NAME=traffic-manager export NAMESPACE=cozy-telepresence -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/velero/Makefile b/packages/system/velero/Makefile index ca4ebd5e..44eba951 100644 --- a/packages/system/velero/Makefile +++ b/packages/system/velero/Makefile @@ -1,7 +1,7 @@ export NAME=velero export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/vertical-pod-autoscaler-crds/Makefile b/packages/system/vertical-pod-autoscaler-crds/Makefile index 9290640e..52b84278 100644 --- a/packages/system/vertical-pod-autoscaler-crds/Makefile +++ b/packages/system/vertical-pod-autoscaler-crds/Makefile @@ -1,7 +1,7 @@ export NAME=vertical-pod-autoscaler export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: curl -o ./templates/vpa-v1-crd-gen.yaml https://raw.githubusercontent.com/kubernetes/autoscaler/refs/heads/master/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml diff --git a/packages/system/vertical-pod-autoscaler/Makefile b/packages/system/vertical-pod-autoscaler/Makefile index 389f9c6e..1e5372e1 100644 --- a/packages/system/vertical-pod-autoscaler/Makefile +++ b/packages/system/vertical-pod-autoscaler/Makefile @@ -1,7 +1,7 @@ export NAME=vertical-pod-autoscaler export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml b/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml index 5fb35fca..6a86d8ab 100644 --- a/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml +++ b/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml @@ -16,15 +16,10 @@ metadata: name: vpa-for-vpa namespace: cozy-vpa-for-vpa spec: - chart: - spec: - chart: cozy-vertical-pod-autoscaler - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-vertical-pod-autoscaler + namespace: cozy-system dependsOn: - name: monitoring-agents namespace: cozy-monitoring diff --git a/packages/system/victoria-metrics-operator/Makefile b/packages/system/victoria-metrics-operator/Makefile index 0f31d7b7..f95bf1db 100644 --- a/packages/system/victoria-metrics-operator/Makefile +++ b/packages/system/victoria-metrics-operator/Makefile @@ -1,7 +1,7 @@ export NAME=victoria-metrics-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/vsnap-crd/Makefile b/packages/system/vsnap-crd/Makefile index 57e5a290..005eefbd 100644 --- a/packages/system/vsnap-crd/Makefile +++ b/packages/system/vsnap-crd/Makefile @@ -1,7 +1,7 @@ export NAME=vsnap-crd export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/pkg/cmd/server/start.go b/pkg/cmd/server/start.go index 3427e61d..1f334cb9 100644 --- a/pkg/cmd/server/start.go +++ b/pkg/cmd/server/start.go @@ -35,14 +35,12 @@ import ( "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/runtime" utilerrors "k8s.io/apimachinery/pkg/util/errors" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/version" "k8s.io/apiserver/pkg/endpoints/openapi" genericapiserver "k8s.io/apiserver/pkg/server" genericoptions "k8s.io/apiserver/pkg/server/options" + "k8s.io/apiserver/pkg/util/compatibility" utilfeature "k8s.io/apiserver/pkg/util/feature" - utilversionpkg "k8s.io/apiserver/pkg/util/version" - "k8s.io/component-base/featuregate" baseversion "k8s.io/component-base/version" netutils "k8s.io/utils/net" "sigs.k8s.io/controller-runtime/pkg/client" @@ -87,7 +85,7 @@ func NewCommandStartCozyServer(ctx context.Context, defaults *CozyServerOptions) Short: "Launch an Cozystack API server", Long: "Launch an Cozystack API server", PersistentPreRunE: func(*cobra.Command, []string) error { - return utilversionpkg.DefaultComponentGlobalsRegistry.Set() + return nil }, RunE: func(c *cobra.Command, args []string) error { if err := o.Complete(); err != nil { @@ -107,38 +105,8 @@ func NewCommandStartCozyServer(ctx context.Context, defaults *CozyServerOptions) flags := cmd.Flags() o.RecommendedOptions.AddFlags(flags) - // The following lines demonstrate how to configure version compatibility and feature gates - // for the "Cozy" component according to KEP-4330. - - // Create a default version object for the "Cozy" component. - defaultCozyVersion := "1.1" - // Register the "Cozy" component in the global component registry, - // associating it with its effective version and feature gate configuration. - _, appsFeatureGate := utilversionpkg.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister( - apiserver.CozyComponentName, utilversionpkg.NewEffectiveVersion(defaultCozyVersion), - featuregate.NewVersionedFeatureGate(version.MustParse(defaultCozyVersion)), - ) - - // Add feature gate specifications for the "Cozy" component. - utilruntime.Must(appsFeatureGate.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{ - // Example of adding feature gates: - // "FeatureName": {{"v1", true}, {"v2", false}}, - })) - - // Register the standard kube component if it is not already registered in the global registry. - _, _ = utilversionpkg.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister( - utilversionpkg.DefaultKubeComponent, - utilversionpkg.NewEffectiveVersion(baseversion.DefaultKubeBinaryVersion), - utilfeature.DefaultMutableFeatureGate, - ) - - // Set the version emulation mapping from the "Cozy" component to the kube component. - utilruntime.Must(utilversionpkg.DefaultComponentGlobalsRegistry.SetEmulationVersionMapping( - apiserver.CozyComponentName, utilversionpkg.DefaultKubeComponent, CozyVersionToKubeVersion, - )) - - // Add flags from the global component registry. - utilversionpkg.DefaultComponentGlobalsRegistry.AddFlags(flags) + // Note: KEP-4330 component versioning functionality (k8s.io/apiserver/pkg/util/version) + // is not available in Kubernetes v0.34.1. The component versioning code has been removed. return cmd } @@ -160,7 +128,7 @@ func (o *CozyServerOptions) Complete() error { return fmt.Errorf("client initialization failed: %w", err) } - crdList := &v1alpha1.CozystackResourceDefinitionList{} + crdList := &v1alpha1.ApplicationDefinitionList{} // Retry with exponential backoff for at least 30 minutes const maxRetryDuration = 30 * time.Minute @@ -178,11 +146,11 @@ func (o *CozyServerOptions) Complete() error { // Check if we've exceeded the maximum retry duration if time.Since(startTime) >= maxRetryDuration { - return fmt.Errorf("failed to list CozystackResourceDefinitions after %v: %w", maxRetryDuration, err) + return fmt.Errorf("failed to list ApplicationDefinitions after %v: %w", maxRetryDuration, err) } // Log the error and wait before retrying - fmt.Printf("Failed to list CozystackResourceDefinitions (retrying in %v): %v\n", delay, err) + fmt.Printf("Failed to list ApplicationDefinitions (retrying in %v): %v\n", delay, err) time.Sleep(delay) delay = time.Duration(float64(delay) * 1.5) @@ -205,16 +173,29 @@ func (o *CozyServerOptions) Complete() error { Release: config.ReleaseConfig{ Prefix: crd.Spec.Release.Prefix, Labels: crd.Spec.Release.Labels, - Chart: config.ChartConfig{ - Name: crd.Spec.Release.Chart.Name, - SourceRef: config.SourceRefConfig{ - Kind: crd.Spec.Release.Chart.SourceRef.Kind, - Name: crd.Spec.Release.Chart.SourceRef.Name, - Namespace: crd.Spec.Release.Chart.SourceRef.Namespace, - }, - }, }, } + + // Handle either Chart or ChartRef (mutually exclusive per CRD validation) + if crd.Spec.Release.Chart != nil { + resource.Release.Chart = &config.ChartConfig{ + Name: crd.Spec.Release.Chart.Name, + SourceRef: config.SourceRefConfig{ + Kind: crd.Spec.Release.Chart.SourceRef.Kind, + Name: crd.Spec.Release.Chart.SourceRef.Name, + Namespace: crd.Spec.Release.Chart.SourceRef.Namespace, + }, + } + } else if crd.Spec.Release.ChartRef != nil { + resource.Release.ChartRef = &config.ChartRefConfig{ + SourceRef: config.SourceRefConfig{ + Kind: crd.Spec.Release.ChartRef.SourceRef.Kind, + Name: crd.Spec.Release.ChartRef.SourceRef.Name, + Namespace: crd.Spec.Release.ChartRef.SourceRef.Namespace, + }, + } + } + o.ResourceConfig.Resources = append(o.ResourceConfig.Resources, resource) } @@ -225,7 +206,6 @@ func (o *CozyServerOptions) Complete() error { func (o CozyServerOptions) Validate(args []string) error { var allErrors []error allErrors = append(allErrors, o.RecommendedOptions.Validate()...) - allErrors = append(allErrors, utilversionpkg.DefaultComponentGlobalsRegistry.Validate()...) return utilerrors.NewAggregate(allErrors) } @@ -281,12 +261,8 @@ func (o *CozyServerOptions) Config() (*apiserver.Config, error) { serverConfig.OpenAPIV3Config.PostProcessSpec = buildPostProcessV3(kindSchemas) - serverConfig.FeatureGate = utilversionpkg.DefaultComponentGlobalsRegistry.FeatureGateFor( - utilversionpkg.DefaultKubeComponent, - ) - serverConfig.EffectiveVersion = utilversionpkg.DefaultComponentGlobalsRegistry.EffectiveVersionFor( - apiserver.CozyComponentName, - ) + serverConfig.FeatureGate = utilfeature.DefaultMutableFeatureGate + serverConfig.EffectiveVersion = compatibility.DefaultBuildEffectiveVersion() if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil { return nil, err @@ -320,15 +296,27 @@ func (o CozyServerOptions) RunCozyServer(ctx context.Context) error { } // CozyVersionToKubeVersion defines the version mapping between the Cozy component and kube +// Note: This function is currently unused as KEP-4330 component versioning is not available +// in Kubernetes v0.34.1. It is kept for potential future use. func CozyVersionToKubeVersion(ver *version.Version) *version.Version { if ver.Major() != 1 { return nil } - kubeVer := utilversionpkg.DefaultKubeEffectiveVersion().BinaryVersion() + kubeVer, err := version.ParseSemantic(baseversion.DefaultKubeBinaryVersion) + if err != nil { + return nil + } // "1.2" corresponds to kubeVer offset := int(ver.Minor()) - 2 - mappedVer := kubeVer.OffsetMinor(offset) - if mappedVer.GreaterThan(kubeVer) { + mappedMinor := int(kubeVer.Minor()) + offset + if mappedMinor < 0 { + return nil + } + mappedVer, err := version.ParseSemantic(fmt.Sprintf("%d.%d.0", kubeVer.Major(), mappedMinor)) + if err != nil { + return nil + } + if mappedVer.AtLeast(kubeVer) { return kubeVer } return mappedVer diff --git a/pkg/cmd/server/start_test.go b/pkg/cmd/server/start_test.go index da96ed31..7a4bd259 100644 --- a/pkg/cmd/server/start_test.go +++ b/pkg/cmd/server/start_test.go @@ -17,16 +17,20 @@ limitations under the License. package server import ( + "fmt" "testing" "k8s.io/apimachinery/pkg/util/version" - utilversion "k8s.io/apiserver/pkg/util/version" + baseversion "k8s.io/component-base/version" "github.com/stretchr/testify/assert" ) func TestCozyEmulationVersionToKubeEmulationVersion(t *testing.T) { - defaultKubeEffectiveVersion := utilversion.DefaultKubeEffectiveVersion() + kubeVer, err := version.ParseSemantic(baseversion.DefaultKubeBinaryVersion) + if err != nil { + t.Fatalf("Failed to parse kube version: %v", err) + } testCases := []struct { desc string @@ -36,22 +40,22 @@ func TestCozyEmulationVersionToKubeEmulationVersion(t *testing.T) { { desc: "same version as than kube binary", appsEmulationVer: version.MajorMinor(1, 2), - expectedKubeEmulationVer: defaultKubeEffectiveVersion.BinaryVersion(), + expectedKubeEmulationVer: kubeVer, }, { desc: "1 version lower than kube binary", appsEmulationVer: version.MajorMinor(1, 1), - expectedKubeEmulationVer: defaultKubeEffectiveVersion.BinaryVersion().OffsetMinor(-1), + expectedKubeEmulationVer: mustParseVersion(t, kubeVer.Major(), kubeVer.Minor()-1), }, { desc: "2 versions lower than kube binary", appsEmulationVer: version.MajorMinor(1, 0), - expectedKubeEmulationVer: defaultKubeEffectiveVersion.BinaryVersion().OffsetMinor(-2), + expectedKubeEmulationVer: mustParseVersion(t, kubeVer.Major(), kubeVer.Minor()-2), }, { desc: "capped at kube binary", appsEmulationVer: version.MajorMinor(1, 3), - expectedKubeEmulationVer: defaultKubeEffectiveVersion.BinaryVersion(), + expectedKubeEmulationVer: kubeVer, }, { desc: "no mapping", @@ -62,7 +66,19 @@ func TestCozyEmulationVersionToKubeEmulationVersion(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { mappedKubeEmulationVer := CozyVersionToKubeVersion(tc.appsEmulationVer) - assert.True(t, mappedKubeEmulationVer.EqualTo(tc.expectedKubeEmulationVer)) + if tc.expectedKubeEmulationVer == nil { + assert.Nil(t, mappedKubeEmulationVer) + } else { + assert.True(t, mappedKubeEmulationVer.EqualTo(tc.expectedKubeEmulationVer)) + } }) } } + +func mustParseVersion(t *testing.T, major, minor uint) *version.Version { + v, err := version.ParseSemantic(fmt.Sprintf("%d.%d.0", major, minor)) + if err != nil { + t.Fatalf("Failed to parse version: %v", err) + } + return v +} diff --git a/pkg/config/apiserver.go b/pkg/config/apiserver.go new file mode 100644 index 00000000..023cba73 --- /dev/null +++ b/pkg/config/apiserver.go @@ -0,0 +1,63 @@ +/* +Copyright 2024 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +// ResourceConfig represents the structure of the configuration file. +type ResourceConfig struct { + Resources []Resource `yaml:"resources"` +} + +// Resource describes an individual resource. +type Resource struct { + Application ApplicationConfig `yaml:"application"` + Release ReleaseConfig `yaml:"release"` +} + +// ApplicationConfig contains the application settings. +type ApplicationConfig struct { + Kind string `yaml:"kind"` + Singular string `yaml:"singular"` + Plural string `yaml:"plural"` + ShortNames []string `yaml:"shortNames"` + OpenAPISchema string `yaml:"openAPISchema"` +} + +// ReleaseConfig contains the release settings. +type ReleaseConfig struct { + Prefix string `yaml:"prefix"` + Labels map[string]string `yaml:"labels"` + Chart *ChartConfig `yaml:"chart,omitempty"` + ChartRef *ChartRefConfig `yaml:"chartRef,omitempty"` +} + +// ChartConfig contains the chart settings (for HelmRepository source). +type ChartConfig struct { + Name string `yaml:"name"` + SourceRef SourceRefConfig `yaml:"sourceRef"` +} + +// ChartRefConfig contains the chart reference settings (for ExternalArtifact source). +type ChartRefConfig struct { + SourceRef SourceRefConfig `yaml:"sourceRef"` +} + +// SourceRefConfig contains the reference to the chart source. +type SourceRefConfig struct { + Kind string `yaml:"kind"` + Name string `yaml:"name"` + Namespace string `yaml:"namespace"` +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 390918a6..0fd39bd6 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -16,42 +16,151 @@ limitations under the License. package config -// ResourceConfig represents the structure of the configuration file. -type ResourceConfig struct { - Resources []Resource `yaml:"resources"` +import "strings" + +// CozystackConfig represents the structure of the cozystack ConfigMap. +type CozystackConfig struct { + // BundleName specifies which bundle to use (e.g., "paas-full", "distro-full", etc.) + BundleName string `yaml:"bundle-name,omitempty"` + + // BundleDisable is a comma-separated list of components to disable + BundleDisable string `yaml:"bundle-disable,omitempty"` + + // BundleEnable is a comma-separated list of optional components to enable + BundleEnable string `yaml:"bundle-enable,omitempty"` + + // OIDCEnabled indicates whether OIDC is enabled (default: "false") + OIDCEnabled string `yaml:"oidc-enabled,omitempty"` + + // RootHost specifies the root host for tenant-root namespace + RootHost string `yaml:"root-host,omitempty"` + + // APIServerEndpoint specifies the API server endpoint + APIServerEndpoint string `yaml:"api-server-endpoint,omitempty"` + + // ClusterDomain specifies the cluster domain (default: "cozy.local") + ClusterDomain string `yaml:"cluster-domain,omitempty"` + + // IPv4PodCIDR specifies the IPv4 pod CIDR + IPv4PodCIDR string `yaml:"ipv4-pod-cidr,omitempty"` + + // IPv4PodGateway specifies the IPv4 pod gateway + IPv4PodGateway string `yaml:"ipv4-pod-gateway,omitempty"` + + // IPv4SvcCIDR specifies the IPv4 service CIDR + IPv4SvcCIDR string `yaml:"ipv4-svc-cidr,omitempty"` + + // IPv4JoinCIDR specifies the IPv4 join CIDR + IPv4JoinCIDR string `yaml:"ipv4-join-cidr,omitempty"` + + // Values contains component-specific values keyed by component name + // Key format: "values-{component-name}" + Values map[string]string `yaml:",inline"` } -// Resource describes an individual resource. -type Resource struct { - Application ApplicationConfig `yaml:"application"` - Release ReleaseConfig `yaml:"release"` +// ParseConfigMapData parses ConfigMap data into CozystackConfig. +func ParseConfigMapData(data map[string]string) *CozystackConfig { + cfg := &CozystackConfig{ + Values: make(map[string]string), + } + + if v, ok := data["bundle-name"]; ok { + cfg.BundleName = v + } + if v, ok := data["bundle-disable"]; ok { + cfg.BundleDisable = v + } + if v, ok := data["bundle-enable"]; ok { + cfg.BundleEnable = v + } + if v, ok := data["oidc-enabled"]; ok { + cfg.OIDCEnabled = v + } + if v, ok := data["root-host"]; ok { + cfg.RootHost = v + } + if v, ok := data["api-server-endpoint"]; ok { + cfg.APIServerEndpoint = v + } + if v, ok := data["cluster-domain"]; ok { + cfg.ClusterDomain = v + } else { + cfg.ClusterDomain = "cozy.local" + } + if v, ok := data["ipv4-pod-cidr"]; ok { + cfg.IPv4PodCIDR = v + } + if v, ok := data["ipv4-pod-gateway"]; ok { + cfg.IPv4PodGateway = v + } + if v, ok := data["ipv4-svc-cidr"]; ok { + cfg.IPv4SvcCIDR = v + } + if v, ok := data["ipv4-join-cidr"]; ok { + cfg.IPv4JoinCIDR = v + } + + // Extract values-* keys + for k, v := range data { + if len(k) > 7 && k[:7] == "values-" { + cfg.Values[k] = v + } + } + + return cfg } -// ApplicationConfig contains the application settings. -type ApplicationConfig struct { - Kind string `yaml:"kind"` - Singular string `yaml:"singular"` - Plural string `yaml:"plural"` - ShortNames []string `yaml:"shortNames"` - OpenAPISchema string `yaml:"openAPISchema"` +// GetComponentValues returns values for a specific component. +func (c *CozystackConfig) GetComponentValues(componentName string) (string, bool) { + key := "values-" + componentName + v, ok := c.Values[key] + return v, ok } -// ReleaseConfig contains the release settings. -type ReleaseConfig struct { - Prefix string `yaml:"prefix"` - Labels map[string]string `yaml:"labels"` - Chart ChartConfig `yaml:"chart"` +// IsComponentDisabled checks if a component is in the disabled list. +func (c *CozystackConfig) IsComponentDisabled(componentName string) bool { + if c.BundleDisable == "" { + return false + } + disabled := splitList(c.BundleDisable) + for _, d := range disabled { + if d == componentName { + return true + } + } + return false } -// ChartConfig contains the chart settings. -type ChartConfig struct { - Name string `yaml:"name"` - SourceRef SourceRefConfig `yaml:"sourceRef"` +// IsComponentEnabled checks if an optional component is in the enabled list. +func (c *CozystackConfig) IsComponentEnabled(componentName string) bool { + if c.BundleEnable == "" { + return false + } + enabled := splitList(c.BundleEnable) + for _, e := range enabled { + if e == componentName { + return true + } + } + return false } -// SourceRefConfig contains the reference to the chart source. -type SourceRefConfig struct { - Kind string `yaml:"kind"` - Name string `yaml:"name"` - Namespace string `yaml:"namespace"` +// IsOIDCEnabled checks if OIDC is enabled. +func (c *CozystackConfig) IsOIDCEnabled() bool { + return c.OIDCEnabled == "true" +} + +// splitList splits a comma-separated string into a slice. +func splitList(s string) []string { + if s == "" { + return nil + } + var result []string + parts := strings.Split(s, ",") + for _, part := range parts { + if trimmed := strings.TrimSpace(part); trimmed != "" { + result = append(result, trimmed) + } + } + return result } diff --git a/pkg/cozylib/namespace.go b/pkg/cozylib/namespace.go new file mode 100644 index 00000000..ea87f535 --- /dev/null +++ b/pkg/cozylib/namespace.go @@ -0,0 +1,96 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cozylib + +import ( + "encoding/json" + "fmt" + "strings" + + corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +const ( + // NamespaceAnnotationPrefix is the prefix for namespace annotations that should be copied to _namespace values + NamespaceAnnotationPrefix = "namespace.cozystack.io/" +) + +// ExtractNamespaceAnnotations extracts namespace.cozystack.io/* annotations from namespace +// and returns them as a map with the prefix removed. +// For example, "namespace.cozystack.io/host" becomes "host" in the returned map. +func ExtractNamespaceAnnotations(ns *corev1.Namespace) map[string]string { + result := make(map[string]string) + prefix := NamespaceAnnotationPrefix + + if ns.Annotations == nil { + return result + } + + for key, value := range ns.Annotations { + if strings.HasPrefix(key, prefix) { + // Remove prefix and add to result + namespaceKey := strings.TrimPrefix(key, prefix) + result[namespaceKey] = value + } + } + + return result +} + +// InjectNamespaceAnnotationsIntoValues injects namespace.cozystack.io/* annotations into _namespace (top-level) in values. +// This function extracts annotations from the namespace and adds them to the _namespace field in the values JSON. +// If namespace is nil or has no matching annotations, values are returned as-is. +func InjectNamespaceAnnotationsIntoValues(values *apiextensionsv1.JSON, ns *corev1.Namespace) (*apiextensionsv1.JSON, error) { + if ns == nil { + return values, nil + } + + // Extract namespace.cozystack.io/* annotations + namespaceLabels := ExtractNamespaceAnnotations(ns) + if len(namespaceLabels) == 0 { + // No namespace annotations, return values as-is + return values, nil + } + + // Parse values + var valuesMap map[string]interface{} + if values != nil && len(values.Raw) > 0 { + if err := json.Unmarshal(values.Raw, &valuesMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal values: %w", err) + } + } else { + valuesMap = make(map[string]interface{}) + } + + // Convert namespaceLabels from map[string]string to map[string]interface{} + namespaceLabelsMap := make(map[string]interface{}) + for k, v := range namespaceLabels { + namespaceLabelsMap[k] = v + } + + // Namespace annotations completely overwrite existing _namespace field (top-level) + valuesMap["_namespace"] = namespaceLabelsMap + + // Marshal back to JSON + mergedJSON, err := json.Marshal(valuesMap) + if err != nil { + return nil, fmt.Errorf("failed to marshal values with namespace annotations: %w", err) + } + + return &apiextensionsv1.JSON{Raw: mergedJSON}, nil +} diff --git a/pkg/cozylib/values.go b/pkg/cozylib/values.go new file mode 100644 index 00000000..29a47d1e --- /dev/null +++ b/pkg/cozylib/values.go @@ -0,0 +1,266 @@ +/* +Copyright 2025 The Cozystack Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cozylib + +import ( + "encoding/json" + "fmt" + "strings" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +// DeepMergeMaps performs a deep merge of two maps. +// Values from override map take precedence, but nested maps are merged recursively. +func DeepMergeMaps(base, override map[string]interface{}) map[string]interface{} { + result := make(map[string]interface{}) + + // Copy base map + for k, v := range base { + result[k] = v + } + + // Merge override map + for k, v := range override { + if baseVal, exists := result[k]; exists { + // If both are maps, recursively merge + if baseMap, ok := baseVal.(map[string]interface{}); ok { + if overrideMap, ok := v.(map[string]interface{}); ok { + result[k] = DeepMergeMaps(baseMap, overrideMap) + continue + } + } + } + // Override takes precedence for non-map values or new keys + result[k] = v + } + + return result +} + +// MergeValues merges two JSON values with deep merge. +// baseValues are merged first, then overrideValues (overrideValues take precedence). +func MergeValues(baseValues, overrideValues *apiextensionsv1.JSON) (*apiextensionsv1.JSON, error) { + var baseMap, overrideMap map[string]interface{} + + if baseValues != nil && len(baseValues.Raw) > 0 { + if err := json.Unmarshal(baseValues.Raw, &baseMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal base values: %w", err) + } + } else { + baseMap = make(map[string]interface{}) + } + + if overrideValues != nil && len(overrideValues.Raw) > 0 { + if err := json.Unmarshal(overrideValues.Raw, &overrideMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal override values: %w", err) + } + } else { + overrideMap = make(map[string]interface{}) + } + + // Deep merge: baseValues first, then overrideValues (overrideValues override) + merged := DeepMergeMaps(baseMap, overrideMap) + + mergedJSON, err := json.Marshal(merged) + if err != nil { + return nil, fmt.Errorf("failed to marshal merged values: %w", err) + } + + return &apiextensionsv1.JSON{Raw: mergedJSON}, nil +} + +// MergeValuesWithCRDPriority merges CRD values with existing values. +// Existing values have priority (user values override defaults), but _cozystack and _namespace +// from CRD completely overwrite existing values. +func MergeValuesWithCRDPriority(crdValues, existingValues *apiextensionsv1.JSON) (*apiextensionsv1.JSON, error) { + // If CRD has no values, preserve existing + if crdValues == nil || len(crdValues.Raw) == 0 { + return existingValues, nil + } + + // If existing has no values, use CRD values + if existingValues == nil || len(existingValues.Raw) == 0 { + return crdValues, nil + } + + var crdMap, existingMap map[string]interface{} + + // Parse CRD values (defaults) + if err := json.Unmarshal(crdValues.Raw, &crdMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal CRD values: %w", err) + } + + // Parse existing HelmRelease values + if err := json.Unmarshal(existingValues.Raw, &existingMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal existing values: %w", err) + } + + // Start with existing values as base (user values take priority) + // Then merge CRD values on top, but _cozystack and _namespace from CRD completely overwrite + merged := DeepMergeMaps(existingMap, crdMap) + + // Explicitly handle "_cozystack" field: CRD values completely overwrite existing + // This ensures _cozystack field from CRD is always used, even if user modified it + if crdCozystack, exists := crdMap["_cozystack"]; exists { + merged["_cozystack"] = crdCozystack + } + + // Explicitly handle "_namespace" field: CRD values completely overwrite existing + // This ensures _namespace field from CRD is always used, even if user modified it + if crdNamespace, exists := crdMap["_namespace"]; exists { + merged["_namespace"] = crdNamespace + } + + mergedJSON, err := json.Marshal(merged) + if err != nil { + return nil, fmt.Errorf("failed to marshal merged values: %w", err) + } + + return &apiextensionsv1.JSON{Raw: mergedJSON}, nil +} + +// RemoveUnderscoreFields recursively removes all fields starting with "_" from values. +// This is used to hide internal fields from API responses. +func RemoveUnderscoreFields(values *apiextensionsv1.JSON) (*apiextensionsv1.JSON, error) { + if values == nil || len(values.Raw) == 0 { + return values, nil + } + + var valuesMap map[string]interface{} + if err := json.Unmarshal(values.Raw, &valuesMap); err != nil { + return nil, fmt.Errorf("failed to unmarshal values: %w", err) + } + + removeUnderscoreFieldsRecursive(valuesMap) + + // Always return at least an empty JSON object, never nil + if len(valuesMap) == 0 { + return &apiextensionsv1.JSON{Raw: []byte("{}")}, nil + } + + cleanedJSON, err := json.Marshal(valuesMap) + if err != nil { + return nil, fmt.Errorf("failed to marshal cleaned values: %w", err) + } + + return &apiextensionsv1.JSON{Raw: cleanedJSON}, nil +} + +// removeUnderscoreFieldsRecursive recursively removes all fields starting with "_" from a map +func removeUnderscoreFieldsRecursive(m map[string]interface{}) { + if m == nil { + return + } + // Collect keys to delete (we can't delete while iterating) + keysToDelete := make([]string, 0) + for k, v := range m { + if strings.HasPrefix(k, "_") { + keysToDelete = append(keysToDelete, k) + } else if nestedMap, ok := v.(map[string]interface{}); ok { + // Recursively process nested maps + removeUnderscoreFieldsRecursive(nestedMap) + } else if nestedArray, ok := v.([]interface{}); ok { + // Process arrays that might contain maps + for _, item := range nestedArray { + if itemMap, ok := item.(map[string]interface{}); ok { + removeUnderscoreFieldsRecursive(itemMap) + } + } + } + } + + // Delete collected keys + for _, k := range keysToDelete { + delete(m, k) + } +} + +// RemoveUnderscoreFieldsFromMap recursively removes all fields starting with "_" from a map. +// This is a variant that works directly with map[string]any (used in defaulting). +func RemoveUnderscoreFieldsFromMap(m map[string]any) { + if m == nil { + return + } + // Collect keys to delete (we can't delete while iterating) + keysToDelete := make([]string, 0) + for k, v := range m { + if strings.HasPrefix(k, "_") { + keysToDelete = append(keysToDelete, k) + } else if nestedMap, ok := v.(map[string]any); ok { + // Recursively process nested maps + RemoveUnderscoreFieldsFromMap(nestedMap) + } else if nestedArray, ok := v.([]any); ok { + // Process arrays that might contain maps + for _, item := range nestedArray { + if itemMap, ok := item.(map[string]any); ok { + RemoveUnderscoreFieldsFromMap(itemMap) + } + } + } + } + + // Delete collected keys + for _, k := range keysToDelete { + delete(m, k) + } +} + +// CheckUnderscoreFields checks if any field starting with "_" exists in user values and returns an error if it does. +// This prevents users from setting internal fields. +func CheckUnderscoreFields(values *apiextensionsv1.JSON) error { + if values == nil || len(values.Raw) == 0 { + return nil + } + + var valuesMap map[string]interface{} + if err := json.Unmarshal(values.Raw, &valuesMap); err != nil { + return fmt.Errorf("failed to unmarshal values: %w", err) + } + + if hasUnderscoreFields(valuesMap) { + return fmt.Errorf("fields starting with '_' are not allowed in user values") + } + + return nil +} + +// hasUnderscoreFields recursively checks if any field starting with "_" exists in a map +func hasUnderscoreFields(m map[string]interface{}) bool { + if m == nil { + return false + } + for k, v := range m { + if strings.HasPrefix(k, "_") { + return true + } + if nestedMap, ok := v.(map[string]interface{}); ok { + if hasUnderscoreFields(nestedMap) { + return true + } + } else if nestedArray, ok := v.([]interface{}); ok { + for _, item := range nestedArray { + if itemMap, ok := item.(map[string]interface{}); ok { + if hasUnderscoreFields(itemMap) { + return true + } + } + } + } + } + return false +} diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 4202bbac..ba5ab71b 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -2714,6 +2714,13 @@ func schema_pkg_apis_meta_v1_DeleteOptions(ref common.ReferenceCallback) common. }, }, }, + "ignoreStoreReadErrorWithClusterBreakingPotential": { + SchemaProps: spec.SchemaProps{ + Description: "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + Type: []string{"boolean"}, + Format: "", + }, + }, }, }, }, @@ -4601,16 +4608,46 @@ func schema_k8sio_apimachinery_pkg_version_Info(ref common.ReferenceCallback) co Properties: map[string]spec.Schema{ "major": { SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Description: "Major is the major version of the binary version", + Default: "", + Type: []string{"string"}, + Format: "", }, }, "minor": { SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Description: "Minor is the minor version of the binary version", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "emulationMajor": { + SchemaProps: spec.SchemaProps{ + Description: "EmulationMajor is the major version of the emulation version", + Type: []string{"string"}, + Format: "", + }, + }, + "emulationMinor": { + SchemaProps: spec.SchemaProps{ + Description: "EmulationMinor is the minor version of the emulation version", + Type: []string{"string"}, + Format: "", + }, + }, + "minCompatibilityMajor": { + SchemaProps: spec.SchemaProps{ + Description: "MinCompatibilityMajor is the major version of the minimum compatibility version", + Type: []string{"string"}, + Format: "", + }, + }, + "minCompatibilityMinor": { + SchemaProps: spec.SchemaProps{ + Description: "MinCompatibilityMinor is the minor version of the minimum compatibility version", + Type: []string{"string"}, + Format: "", }, }, "gitVersion": { diff --git a/pkg/lineage/lineage.go b/pkg/lineage/lineage.go index 867e1aab..cb99a827 100644 --- a/pkg/lineage/lineage.go +++ b/pkg/lineage/lineage.go @@ -134,12 +134,16 @@ func WalkOwnershipGraph( labels := obj.GetLabels() name, ok := labels[HRLabel] if !ok { + l.V(1).Info("object does not have helm.toolkit.fluxcd.io/name label", "labels", labels) return } + l.V(1).Info("found helm.toolkit.fluxcd.io/name label", "name", name, "namespace", obj.GetNamespace()) ownerObj, err := getUnstructuredObject(ctx, client, mapper, HRAPIVersion, HRKind, obj.GetNamespace(), name) if err != nil { + l.Error(err, "failed to get HelmRelease", "name", name, "namespace", obj.GetNamespace()) return } + l.V(1).Info("found HelmRelease owner", "name", name, "namespace", obj.GetNamespace()) out = append(out, WalkOwnershipGraph(ctx, client, mapper, appMapper, ownerObj, visited)...) return diff --git a/pkg/registry/apps/application/rest.go b/pkg/registry/apps/application/rest.go index 03de7c08..3feef2e4 100644 --- a/pkg/registry/apps/application/rest.go +++ b/pkg/registry/apps/application/rest.go @@ -33,6 +33,7 @@ import ( labels "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/util/duration" "k8s.io/apimachinery/pkg/watch" "k8s.io/apiserver/pkg/endpoints/request" @@ -40,13 +41,17 @@ import ( "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1" "github.com/cozystack/cozystack/pkg/config" + "github.com/cozystack/cozystack/pkg/cozylib" internalapiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" // Importing API errors package to construct appropriate error responses + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" ) @@ -166,6 +171,13 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation helmRelease.Labels = mergeMaps(r.releaseConfig.Labels, helmRelease.Labels) // Merge user labels with prefix helmRelease.Labels = mergeMaps(helmRelease.Labels, addPrefixedMap(app.Labels, LabelPrefix)) + // Add application metadata labels + if helmRelease.Labels == nil { + helmRelease.Labels = make(map[string]string) + } + helmRelease.Labels["apps.cozystack.io/application.kind"] = r.kindName + helmRelease.Labels["apps.cozystack.io/application.group"] = r.gvk.Group + helmRelease.Labels["apps.cozystack.io/application.name"] = app.Name // Note: Annotations from config are not handled as r.releaseConfig.Annotations is undefined klog.V(6).Infof("Creating HelmRelease %s in namespace %s", helmRelease.Name, app.Namespace) @@ -224,9 +236,10 @@ func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions) return nil, err } - // Check if HelmRelease meets the required chartName and sourceRef criteria - if !r.shouldIncludeHelmRelease(helmRelease) { - klog.Errorf("HelmRelease %s does not match the required chartName and sourceRef criteria", helmReleaseName) + // Check if HelmRelease has required labels + if helmRelease.Labels["apps.cozystack.io/application.kind"] != r.kindName || + helmRelease.Labels["apps.cozystack.io/application.group"] != r.gvk.Group { + klog.Errorf("HelmRelease %s does not match the required application labels", helmReleaseName) // Return a NotFound error for the Application resource return nil, apierrors.NewNotFound(r.gvr.GroupResource(), name) } @@ -299,6 +312,19 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption } // Process label.selector + // Always add application metadata label requirements + appKindReq, err := labels.NewRequirement("apps.cozystack.io/application.kind", selection.Equals, []string{r.kindName}) + if err != nil { + klog.Errorf("Error creating application kind label requirement: %v", err) + return nil, fmt.Errorf("error creating application kind label requirement: %v", err) + } + appGroupReq, err := labels.NewRequirement("apps.cozystack.io/application.group", selection.Equals, []string{r.gvk.Group}) + if err != nil { + klog.Errorf("Error creating application group label requirement: %v", err) + return nil, fmt.Errorf("error creating application group label requirement: %v", err) + } + labelRequirements := []labels.Requirement{*appKindReq, *appGroupReq} + if options.LabelSelector != nil { ls := options.LabelSelector.String() parsedLabels, err := labels.Parse(ls) @@ -318,9 +344,12 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption } prefixedReqs = append(prefixedReqs, *prefixedReq) } - helmLabelSelector = labels.NewSelector().Add(prefixedReqs...).String() + labelRequirements = append(labelRequirements, prefixedReqs...) } } + helmLabelSelector = labels.NewSelector().Add(labelRequirements...).String() + + klog.V(6).Infof("Using label selector: %s for kind: %s, group: %s", helmLabelSelector, r.kindName, r.gvk.Group) // Set ListOptions for HelmRelease with selector mapping metaOptions := metav1.ListOptions{ @@ -339,18 +368,30 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption return nil, err } + klog.V(6).Infof("Found %d HelmReleases with label selector, filtering by labels...", len(hrList.Items)) + // Initialize unstructured items array items := make([]unstructured.Unstructured, 0) // Iterate over HelmReleases and convert to Applications + // Filter by labels to ensure only relevant HelmReleases are included + // This is a safety check in case label selectors don't work perfectly or HelmReleases were created without labels for i := range hrList.Items { - if !r.shouldIncludeHelmRelease(&hrList.Items[i]) { + hr := &hrList.Items[i] + + // Verify that HelmRelease has the required labels + if hr.Labels["apps.cozystack.io/application.kind"] != r.kindName || + hr.Labels["apps.cozystack.io/application.group"] != r.gvk.Group { + klog.V(6).Infof("Skipping HelmRelease %s - missing or incorrect application labels (kind: %s, expected: %s, group: %s, expected: %s)", + hr.GetName(), + hr.Labels["apps.cozystack.io/application.kind"], r.kindName, + hr.Labels["apps.cozystack.io/application.group"], r.gvk.Group) continue } - app, err := r.ConvertHelmReleaseToApplication(&hrList.Items[i]) + app, err := r.ConvertHelmReleaseToApplication(hr) if err != nil { - klog.Errorf("Error converting HelmRelease %s to Application: %v", hrList.Items[i].GetName(), err) + klog.Errorf("Error converting HelmRelease %s to Application: %v", hr.GetName(), err) continue } @@ -482,14 +523,22 @@ func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje helmRelease.Labels = mergeMaps(r.releaseConfig.Labels, helmRelease.Labels) // Merge user labels with prefix helmRelease.Labels = mergeMaps(helmRelease.Labels, addPrefixedMap(app.Labels, LabelPrefix)) + // Add application metadata labels + if helmRelease.Labels == nil { + helmRelease.Labels = make(map[string]string) + } + helmRelease.Labels["apps.cozystack.io/application.kind"] = r.kindName + helmRelease.Labels["apps.cozystack.io/application.group"] = r.gvk.Group + helmRelease.Labels["apps.cozystack.io/application.name"] = app.Name // Note: Annotations from config are not handled as r.releaseConfig.Annotations is undefined klog.V(6).Infof("Updating HelmRelease %s in namespace %s", helmRelease.Name, helmRelease.Namespace) - // Before updating, ensure the HelmRelease meets the inclusion criteria + // Before updating, ensure the HelmRelease has required labels // This prevents updating HelmReleases that should not be managed as Applications - if !r.shouldIncludeHelmRelease(helmRelease) { - klog.Errorf("HelmRelease %s does not match the required chartName and sourceRef criteria", helmRelease.Name) + if helmRelease.Labels["apps.cozystack.io/application.kind"] != r.kindName || + helmRelease.Labels["apps.cozystack.io/application.group"] != r.gvk.Group { + klog.Errorf("HelmRelease %s does not match the required application labels", helmRelease.Name) // Return a NotFound error for the Application resource return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name) } @@ -501,9 +550,10 @@ func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje return nil, false, fmt.Errorf("failed to update HelmRelease: %v", err) } - // After updating, ensure the updated HelmRelease still meets the inclusion criteria - if !r.shouldIncludeHelmRelease(helmRelease) { - klog.Errorf("Updated HelmRelease %s does not match the required chartName and sourceRef criteria", helmRelease.GetName()) + // After updating, ensure the updated HelmRelease still has required labels + if helmRelease.Labels["apps.cozystack.io/application.kind"] != r.kindName || + helmRelease.Labels["apps.cozystack.io/application.group"] != r.gvk.Group { + klog.Errorf("Updated HelmRelease %s does not match the required application labels", helmRelease.GetName()) // Return a NotFound error for the Application resource return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name) } @@ -797,28 +847,38 @@ func (cw *customWatcher) ResultChan() <-chan watch.Event { // shouldIncludeHelmRelease determines if a HelmRelease should be included based on filtering criteria func (r *REST) shouldIncludeHelmRelease(hr *helmv2.HelmRelease) bool { - // Nil check for Chart field - if hr.Spec.Chart == nil { - klog.V(6).Infof("HelmRelease %s has nil spec.chart field", hr.GetName()) + // Check if using chart (HelmRepository) or chartRef (ExternalArtifact) + if hr.Spec.Chart != nil { + // Using chart (HelmRepository) + if r.releaseConfig.Chart == nil { + return false + } + // Filter by Chart Name + chartName := hr.Spec.Chart.Spec.Chart + if chartName == "" { + klog.V(6).Infof("HelmRelease %s missing spec.chart.spec.chart field", hr.GetName()) + return false + } + if chartName != r.releaseConfig.Chart.Name { + klog.V(6).Infof("HelmRelease %s chart name %s does not match expected %s", hr.GetName(), chartName, r.releaseConfig.Chart.Name) + return false + } + // Filter by SourceRefConfig and Prefix + return r.matchesSourceRefAndPrefix(hr) + } else if hr.Spec.ChartRef != nil { + // Using chartRef (ExternalArtifact) + if r.releaseConfig.ChartRef == nil { + return false + } + // Filter by ChartRef SourceRef and Prefix + return r.matchesChartRefAndPrefix(hr) + } else { + klog.V(6).Infof("HelmRelease %s has neither spec.chart nor spec.chartRef field", hr.GetName()) return false } - - // Filter by Chart Name - chartName := hr.Spec.Chart.Spec.Chart - if chartName == "" { - klog.V(6).Infof("HelmRelease %s missing spec.chart.spec.chart field", hr.GetName()) - return false - } - if chartName != r.releaseConfig.Chart.Name { - klog.V(6).Infof("HelmRelease %s chart name %s does not match expected %s", hr.GetName(), chartName, r.releaseConfig.Chart.Name) - return false - } - - // Filter by SourceRefConfig and Prefix - return r.matchesSourceRefAndPrefix(hr) } -// matchesSourceRefAndPrefix checks both SourceRefConfig and Prefix criteria +// matchesSourceRefAndPrefix checks both SourceRefConfig and Prefix criteria for chart (HelmRepository) func (r *REST) matchesSourceRefAndPrefix(hr *helmv2.HelmRelease) bool { // Nil check for Chart field (defensive) if hr.Spec.Chart == nil { @@ -863,6 +923,51 @@ func (r *REST) matchesSourceRefAndPrefix(hr *helmv2.HelmRelease) bool { return true } +// matchesChartRefAndPrefix checks both ChartRef SourceRef and Prefix criteria for chartRef (ExternalArtifact) +func (r *REST) matchesChartRefAndPrefix(hr *helmv2.HelmRelease) bool { + // Nil check for ChartRef field (defensive) + if hr.Spec.ChartRef == nil { + klog.V(6).Infof("HelmRelease %s has nil spec.chartRef field", hr.GetName()) + return false + } + + // Extract SourceRef fields + sourceRef := hr.Spec.ChartRef + sourceRefKind := sourceRef.Kind + sourceRefName := sourceRef.Name + sourceRefNamespace := sourceRef.Namespace + + if sourceRefKind == "" { + klog.V(6).Infof("HelmRelease %s missing spec.chartRef.kind field", hr.GetName()) + return false + } + if sourceRefName == "" { + klog.V(6).Infof("HelmRelease %s missing spec.chartRef.name field", hr.GetName()) + return false + } + if sourceRefNamespace == "" { + klog.V(6).Infof("HelmRelease %s missing spec.chartRef.namespace field", hr.GetName()) + return false + } + + // Check if SourceRef matches the configuration + if sourceRefKind != r.releaseConfig.ChartRef.SourceRef.Kind || + sourceRefName != r.releaseConfig.ChartRef.SourceRef.Name || + sourceRefNamespace != r.releaseConfig.ChartRef.SourceRef.Namespace { + klog.V(6).Infof("HelmRelease %s chartRef sourceRef does not match expected values", hr.GetName()) + return false + } + + // Additional filtering by Prefix + name := hr.GetName() + if !strings.HasPrefix(name, r.releaseConfig.Prefix) { + klog.V(6).Infof("HelmRelease %s does not have the expected prefix %s", name, r.releaseConfig.Prefix) + return false + } + + return true +} + // getNamespace extracts the namespace from the context func (r *REST) getNamespace(ctx context.Context) (string, error) { namespace, ok := request.NamespaceFrom(ctx) @@ -931,6 +1036,23 @@ func filterPrefixedMap(original map[string]string, prefix string) map[string]str return processed } +// getApplicationDefinition retrieves the ApplicationDefinition for this resource kind +func (r *REST) getApplicationDefinition(ctx context.Context) (*cozyv1alpha1.ApplicationDefinition, error) { + crdList := &cozyv1alpha1.ApplicationDefinitionList{} + if err := r.c.List(ctx, crdList); err != nil { + return nil, fmt.Errorf("failed to list ApplicationDefinitions: %w", err) + } + + for i := range crdList.Items { + crd := &crdList.Items[i] + if crd.Spec.Application.Kind == r.kindName { + return crd, nil + } + } + + return nil, fmt.Errorf("ApplicationDefinition not found for kind %s", r.kindName) +} + // ConvertHelmReleaseToApplication converts a HelmRelease to an Application func (r *REST) ConvertHelmReleaseToApplication(hr *helmv2.HelmRelease) (appsv1alpha1.Application, error) { klog.V(6).Infof("Converting HelmRelease to Application for resource %s", hr.GetName()) @@ -946,6 +1068,39 @@ func (r *REST) ConvertHelmReleaseToApplication(hr *helmv2.HelmRelease) (appsv1al return app, fmt.Errorf("defaulting error: %w", err) } + // Remove all fields starting with "_" after applying defaults to ensure they're not shown to user + // This must be done after applySpecDefaults because defaults might add them back + if app.Spec != nil && len(app.Spec.Raw) > 0 { + cleanedValues, err := cozylib.RemoveUnderscoreFields(app.Spec) + if err != nil { + return app, fmt.Errorf("failed to remove underscore fields from values: %w", err) + } + app.Spec = cleanedValues + + // Double-check that underscore fields were removed + if app.Spec != nil && len(app.Spec.Raw) > 0 { + var verifyMap map[string]interface{} + if err := json.Unmarshal(app.Spec.Raw, &verifyMap); err == nil { + // Check for any remaining underscore fields + for k := range verifyMap { + if strings.HasPrefix(k, "_") { + klog.Errorf("CRITICAL: Field %s still exists after removal! Removing forcefully...", k) + delete(verifyMap, k) + } + } + // Re-marshal if we removed anything + if len(verifyMap) == 0 { + app.Spec = &apiextensionsv1.JSON{Raw: []byte("{}")} + } else { + forceCleanedJSON, err := json.Marshal(verifyMap) + if err == nil { + app.Spec = &apiextensionsv1.JSON{Raw: forceCleanedJSON} + } + } + } + } + } + klog.V(6).Infof("Successfully converted HelmRelease %s to Application", hr.GetName()) return app, nil } @@ -957,6 +1112,13 @@ func (r *REST) ConvertApplicationToHelmRelease(app *appsv1alpha1.Application) (* // convertHelmReleaseToApplication implements the actual conversion logic func (r *REST) convertHelmReleaseToApplication(hr *helmv2.HelmRelease) (appsv1alpha1.Application, error) { + // Remove all fields starting with "_" from values before setting spec to ensure they never appear in spec + cleanedValues, err := cozylib.RemoveUnderscoreFields(hr.Spec.Values) + if err != nil { + // If removal fails, use original values (shouldn't happen, but be safe) + cleanedValues = hr.Spec.Values + } + app := appsv1alpha1.Application{ TypeMeta: metav1.TypeMeta{ APIVersion: "apps.cozystack.io/v1alpha1", @@ -972,9 +1134,9 @@ func (r *REST) convertHelmReleaseToApplication(hr *helmv2.HelmRelease) (appsv1al Labels: filterPrefixedMap(hr.Labels, LabelPrefix), Annotations: filterPrefixedMap(hr.Annotations, AnnotationPrefix), }, - Spec: hr.Spec.Values, + Spec: cleanedValues, Status: appsv1alpha1.ApplicationStatus{ - Version: hr.Status.LastAttemptedRevision, + Version: extractRevision(hr.Status.LastAttemptedRevision), }, } @@ -1002,6 +1164,50 @@ func (r *REST) convertHelmReleaseToApplication(hr *helmv2.HelmRelease) (appsv1al // convertApplicationToHelmRelease implements the actual conversion logic func (r *REST) convertApplicationToHelmRelease(app *appsv1alpha1.Application) (*helmv2.HelmRelease, error) { + ctx := context.Background() + + // Check if user specified any field starting with "_" in values + if err := cozylib.CheckUnderscoreFields(app.Spec); err != nil { + return nil, err + } + + // Get ApplicationDefinition to extract default values + crd, err := r.getApplicationDefinition(ctx) + if err != nil { + klog.V(6).Infof("Could not find ApplicationDefinition for kind %s: %v", r.kindName, err) + // Continue without default values if CRD not found + crd = nil + } + + // Start with default values from CRD (if any) + var mergedValues *apiextensionsv1.JSON + if crd != nil && crd.Spec.Release.Values != nil { + mergedValues, err = cozylib.MergeValues(crd.Spec.Release.Values, app.Spec) + if err != nil { + return nil, fmt.Errorf("failed to merge default values with user values: %w", err) + } + } else { + // No default values, use user values as-is + mergedValues = app.Spec + } + + // Get namespace to extract namespace.cozystack.io/* labels + namespace := &corev1.Namespace{} + if err := r.c.Get(ctx, client.ObjectKey{Name: app.Namespace}, namespace); err != nil { + klog.V(6).Infof("Could not get namespace %s: %v", app.Namespace, err) + // Continue without namespace labels if namespace not found + namespace = nil + } + + // Extract namespace annotations and add to _namespace (top-level) + if namespace != nil { + var err error + mergedValues, err = cozylib.InjectNamespaceAnnotationsIntoValues(mergedValues, namespace) + if err != nil { + return nil, fmt.Errorf("failed to inject namespace annotations: %w", err) + } + } + helmRelease := &helmv2.HelmRelease{ TypeMeta: metav1.TypeMeta{ APIVersion: "helm.toolkit.fluxcd.io/v2", @@ -1016,18 +1222,6 @@ func (r *REST) convertApplicationToHelmRelease(app *appsv1alpha1.Application) (* UID: app.ObjectMeta.UID, }, Spec: helmv2.HelmReleaseSpec{ - Chart: &helmv2.HelmChartTemplate{ - Spec: helmv2.HelmChartTemplateSpec{ - Chart: r.releaseConfig.Chart.Name, - Version: ">= 0.0.0-0", - ReconcileStrategy: "Revision", - SourceRef: helmv2.CrossNamespaceObjectReference{ - Kind: r.releaseConfig.Chart.SourceRef.Kind, - Name: r.releaseConfig.Chart.SourceRef.Name, - Namespace: r.releaseConfig.Chart.SourceRef.Namespace, - }, - }, - }, Interval: metav1.Duration{Duration: 5 * time.Minute}, Install: &helmv2.Install{ Remediation: &helmv2.InstallRemediation{ @@ -1039,10 +1233,36 @@ func (r *REST) convertApplicationToHelmRelease(app *appsv1alpha1.Application) (* Retries: -1, }, }, - Values: app.Spec, + Values: mergedValues, }, } + // Set Chart or ChartRef based on configuration + if r.releaseConfig.Chart != nil { + // Using chart (HelmRepository) + helmRelease.Spec.Chart = &helmv2.HelmChartTemplate{ + Spec: helmv2.HelmChartTemplateSpec{ + Chart: r.releaseConfig.Chart.Name, + Version: ">= 0.0.0-0", + ReconcileStrategy: "Revision", + SourceRef: helmv2.CrossNamespaceObjectReference{ + Kind: r.releaseConfig.Chart.SourceRef.Kind, + Name: r.releaseConfig.Chart.SourceRef.Name, + Namespace: r.releaseConfig.Chart.SourceRef.Namespace, + }, + }, + } + } else if r.releaseConfig.ChartRef != nil { + // Using chartRef (ExternalArtifact) + helmRelease.Spec.ChartRef = &helmv2.CrossNamespaceSourceReference{ + Kind: r.releaseConfig.ChartRef.SourceRef.Kind, + Name: r.releaseConfig.ChartRef.SourceRef.Name, + Namespace: r.releaseConfig.ChartRef.SourceRef.Namespace, + } + } else { + return nil, fmt.Errorf("either chart or chartRef must be specified in release config") + } + return helmRelease, nil } @@ -1180,6 +1400,22 @@ func (r *REST) buildTableFromApplication(app appsv1alpha1.Application) metav1.Ta return table } +// extractRevision extracts only the revision part from a version string +// If version is in format "0.1.4+abcdef", returns "abcdef" +// Otherwise returns the original string +func extractRevision(version string) string { + if version == "" { + return "" + } + // Check if version contains "+" separator + if idx := strings.LastIndex(version, "+"); idx >= 0 && idx < len(version)-1 { + // Return only the part after "+" + return version[idx+1:] + } + // If no "+" found, return original version + return version +} + // getVersion returns the application version or a placeholder if unknown func getVersion(version string) string { if version == "" { diff --git a/pkg/registry/apps/application/rest_defaulting.go b/pkg/registry/apps/application/rest_defaulting.go index 6630a08b..dc0b31c0 100644 --- a/pkg/registry/apps/application/rest_defaulting.go +++ b/pkg/registry/apps/application/rest_defaulting.go @@ -19,10 +19,13 @@ package application import ( "encoding/json" "fmt" + "strings" appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" + + "github.com/cozystack/cozystack/pkg/cozylib" ) // applySpecDefaults applies default values to the Application spec based on the schema @@ -39,17 +42,32 @@ func (r *REST) applySpecDefaults(app *appsv1alpha1.Application) error { if m == nil { m = map[string]any{} } + // Remove all fields starting with "_" BEFORE applying defaults to prevent them from being processed + cozylib.RemoveUnderscoreFieldsFromMap(m) + if err := defaultLikeKubernetes(&m, r.specSchema); err != nil { return err } + + // Remove all fields starting with "_" AFTER applying defaults to ensure they're never in the output + // This is a safety measure in case defaults added them back + cozylib.RemoveUnderscoreFieldsFromMap(m) + + // Always return at least an empty JSON object, never nil + if len(m) == 0 { + app.Spec = &apiextensionsv1.JSON{Raw: []byte("{}")} + return nil + } + raw, err := json.Marshal(m) if err != nil { return err } - app.Spec = &apiextv1.JSON{Raw: raw} + app.Spec = &apiextensionsv1.JSON{Raw: raw} return nil } + func defaultLikeKubernetes(root *map[string]any, s *structuralschema.Structural) error { v := any(*root) nv, err := applyDefaults(v, s, true) @@ -116,6 +134,10 @@ func applyDefaults(v any, s *structuralschema.Structural, top bool) (any, error) if s.AdditionalProperties != nil && s.AdditionalProperties.Structural != nil { ap := s.AdditionalProperties.Structural for k, cur := range mv { + // Skip fields starting with "_" - they are internal and should not be processed + if strings.HasPrefix(k, "_") { + continue + } if _, isKnown := s.Properties[k]; isKnown { continue } diff --git a/scripts/installer.sh b/scripts/installer.sh deleted file mode 100755 index 8dfa68ee..00000000 --- a/scripts/installer.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/sh -set -o pipefail -set -e - -BUNDLE=$(set -x; kubectl get configmap -n cozy-system cozystack -o 'go-template={{index .data "bundle-name"}}') -VERSION=$(find scripts/migrations -mindepth 1 -maxdepth 1 -type f | sort -V | awk -F/ 'END {print $NF+1}') - -run_migrations() { - if ! kubectl get configmap -n cozy-system cozystack-version; then - kubectl create configmap -n cozy-system cozystack-version --from-literal=version="$VERSION" --dry-run=client -o yaml | kubectl create -f- - return - fi - current_version=$(kubectl get configmap -n cozy-system cozystack-version -o jsonpath='{.data.version}') || true - until [ "$current_version" = "$VERSION" ]; do - echo "run migration: $current_version --> $VERSION" - chmod +x scripts/migrations/$current_version - scripts/migrations/$current_version - current_version=$(kubectl get configmap -n cozy-system cozystack-version -o jsonpath='{.data.version}') - done -} - -flux_is_ok() { - kubectl wait --for=condition=available -n cozy-fluxcd deploy/source-controller deploy/helm-controller --timeout=1s - kubectl wait --for=condition=ready -n cozy-fluxcd helmrelease/fluxcd --timeout=1s # to call "apply resume" below -} - -ensure_fluxcd() { - if flux_is_ok; then - return - fi - # Install fluxcd-operator - if kubectl get helmreleases.helm.toolkit.fluxcd.io -n cozy-fluxcd fluxcd-operator; then - make -C packages/system/fluxcd-operator apply resume - else - make -C packages/system/fluxcd-operator apply-locally - fi - wait_for_crds fluxinstances.fluxcd.controlplane.io - - # Install fluxcd - if kubectl get helmreleases.helm.toolkit.fluxcd.io -n cozy-fluxcd fluxcd; then - make -C packages/system/fluxcd apply resume - else - make -C packages/system/fluxcd apply-locally - fi - wait_for_crds helmreleases.helm.toolkit.fluxcd.io helmrepositories.source.toolkit.fluxcd.io -} - -wait_for_crds() { - timeout 60 sh -c "until kubectl get crd $*; do sleep 1; done" -} - -install_basic_charts() { - if [ "$BUNDLE" = "paas-full" ] || [ "$BUNDLE" = "distro-full" ]; then - make -C packages/system/cilium apply resume - fi - if [ "$BUNDLE" = "paas-full" ]; then - make -C packages/system/kubeovn apply resume - fi -} - -cd "$(dirname "$0")/.." - -# Run migrations -run_migrations - -# Install namespaces -make -C packages/core/platform namespaces-apply - -# Install fluxcd -ensure_fluxcd - -# Install platform chart -make -C packages/core/platform reconcile - -# Install basic charts -if ! flux_is_ok; then - install_basic_charts -fi - -# Reconcile Helm repositories -kubectl annotate helmrepositories.source.toolkit.fluxcd.io -A -l cozystack.io/repository reconcile.fluxcd.io/requestedAt=$(date +"%Y-%m-%dT%H:%M:%SZ") --overwrite - -# Unsuspend all Cozystack managed charts -kubectl get hr -A -o go-template='{{ range .items }}{{ if .spec.suspend }}{{ .spec.chart.spec.sourceRef.namespace }}/{{ .spec.chart.spec.sourceRef.name }} {{ .metadata.namespace }} {{ .metadata.name }}{{ "\n" }}{{ end }}{{ end }}' | while read repo namespace name; do - case "$repo" in - cozy-system/cozystack-system|cozy-public/cozystack-extra|cozy-public/cozystack-apps) - kubectl patch hr -n "$namespace" "$name" -p '{"spec": {"suspend": null}}' --type=merge --field-manager=flux-client-side-apply - ;; - esac -done - -# Update all Cozystack managed charts to latest version -kubectl get hr -A -l cozystack.io/ui=true --no-headers | awk '{print "kubectl patch helmrelease -n " $1 " " $2 " --type=merge -p '\''{\"spec\":{\"chart\":{\"spec\":{\"version\":\">= 0.0.0-0\"}}}}'\'' "}' | sh -x - -# Reconcile platform chart -trap 'exit' INT TERM -while true; do - sleep 60 & wait - make -C packages/core/platform reconcile -done