diff --git a/api/v1alpha1/cozystackresourcedefinitions_types.go b/api/v1alpha1/cozystackresourcedefinitions_types.go index b2baee85..2f25fb2d 100644 --- a/api/v1alpha1/cozystackresourcedefinitions_types.go +++ b/api/v1alpha1/cozystackresourcedefinitions_types.go @@ -92,7 +92,8 @@ type CozystackResourceDefinitionApplication struct { type CozystackResourceDefinitionRelease struct { // Helm chart configuration - Chart CozystackResourceDefinitionChart `json:"chart"` + // +optional + Chart CozystackResourceDefinitionChart `json:"chart,omitempty"` // Labels for the release Labels map[string]string `json:"labels,omitempty"` // Prefix for the release name @@ -110,17 +111,18 @@ type CozystackResourceDefinitionRelease struct { // - {{ .namespace }}: The namespace of the resource being processed // // Example YAML: -// secrets: -// include: -// - matchExpressions: -// - key: badlabel -// operator: DoesNotExist -// matchLabels: -// goodlabel: goodvalue -// resourceNames: -// - "{{ .name }}-secret" -// - "{{ .kind }}-{{ .name }}-tls" -// - "specificname" +// +// secrets: +// include: +// - matchExpressions: +// - key: badlabel +// operator: DoesNotExist +// matchLabels: +// goodlabel: goodvalue +// resourceNames: +// - "{{ .name }}-secret" +// - "{{ .kind }}-{{ .name }}-tls" +// - "specificname" type CozystackResourceDefinitionResourceSelector struct { metav1.LabelSelector `json:",inline"` // ResourceNames is a list of resource names to match diff --git a/api/v1alpha1/package_types.go b/api/v1alpha1/package_types.go new file mode 100644 index 00000000..dafb8747 --- /dev/null +++ b/api/v1alpha1/package_types.go @@ -0,0 +1,89 @@ +/* +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={pkg,pkgs} +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Variant",type="string",JSONPath=".spec.variant",description="Selected variant" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status",description="Ready status" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].message",description="Ready message" + +// Package is the Schema for the packages API +type Package struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PackageSpec `json:"spec,omitempty"` + Status PackageStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// PackageList contains a list of Packages +type PackageList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Package `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Package{}, &PackageList{}) +} + +// PackageSpec defines the desired state of Package +type PackageSpec struct { + // Variant is the name of the variant to use from the PackageSource + // If not specified, defaults to "default" + // +optional + Variant string `json:"variant,omitempty"` + + // IgnoreDependencies is a list of package source dependencies to ignore + // Dependencies listed here will not be installed even if they are specified in the PackageSource + // +optional + IgnoreDependencies []string `json:"ignoreDependencies,omitempty"` + + // Components is a map of release name to component overrides + // Allows overriding values and enabling/disabling specific components from the PackageSource + // +optional + Components map[string]PackageComponent `json:"components,omitempty"` +} + +// PackageRelease defines overrides for a specific component +type PackageComponent struct { + // Enabled indicates whether this component should be installed + // If false, the component will be disabled even if it's defined in the PackageSource + // +optional + Enabled *bool `json:"enabled,omitempty"` + + // Values contains Helm chart values as a JSON object + // These values will be merged with the default values from the PackageSource + // +optional + Values *apiextensionsv1.JSON `json:"values,omitempty"` +} + +// PackageStatus defines the observed state of Package +type PackageStatus struct { + // Conditions represents the latest available observations of a Package's state + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` +} diff --git a/api/v1alpha1/packagesource_types.go b/api/v1alpha1/packagesource_types.go new file mode 100644 index 00000000..88940c91 --- /dev/null +++ b/api/v1alpha1/packagesource_types.go @@ -0,0 +1,184 @@ +/* +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 ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster,shortName={pkgsrc,pkgsrcs} +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Variants",type="string",JSONPath=".status.variants",description="Package variants (comma-separated)" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status",description="Ready status" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].message",description="Ready message" + +// PackageSource is the Schema for the packagesources API +type PackageSource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PackageSourceSpec `json:"spec,omitempty"` + Status PackageSourceStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// PackageSourceList contains a list of PackageSources +type PackageSourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PackageSource `json:"items"` +} + +func init() { + SchemeBuilder.Register(&PackageSource{}, &PackageSourceList{}) +} + +// PackageSourceSpec defines the desired state of PackageSource +type PackageSourceSpec struct { + // SourceRef is the source reference for the package source charts + // +optional + SourceRef *PackageSourceRef `json:"sourceRef,omitempty"` + + // Variants is a list of package source variants + // Each variant defines components, applications, dependencies, and libraries for a specific configuration + // +optional + Variants []Variant `json:"variants,omitempty"` +} + +// Variant defines a single variant configuration +type Variant struct { + // Name is the unique identifier for this variant + // +required + Name string `json:"name"` + + // DependsOn is a list of package source dependencies + // For example: "cozystack.networking" + // +optional + DependsOn []string `json:"dependsOn,omitempty"` + + // Libraries is a list of Helm library charts used by components in this variant + // +optional + Libraries []Library `json:"libraries,omitempty"` + + // Components is a list of Helm releases to be installed as part of this variant + // +optional + Components []Component `json:"components,omitempty"` +} + +// DependencyTarget defines a named group of packages that can be referenced +// by other package sources via dependsOn +type DependencyTarget 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"` +} + +// Library defines a Helm library chart +type Library struct { + // Name is the optional name for library placed in charts + // +optional + Name string `json:"name,omitempty"` + + // Path is the path to the library chart directory + // +required + Path string `json:"path"` +} + +// PackageSourceRef defines the source reference for package source charts +type PackageSourceRef 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"` + + // Path 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 + Path string `json:"path,omitempty"` +} + +// ComponentInstall defines installation parameters for a component +type ComponentInstall struct { + // ReleaseName is the name of the HelmRelease resource that will be created + // If not specified, defaults to the component Name field + // +optional + ReleaseName string `json:"releaseName,omitempty"` + + // Namespace is the Kubernetes namespace where the release will be installed + // +optional + Namespace string `json:"namespace,omitempty"` + + // Privileged indicates whether this release requires privileged access + // +optional + Privileged bool `json:"privileged,omitempty"` + + // DependsOn is a list of component names that must be installed before this component + // +optional + DependsOn []string `json:"dependsOn,omitempty"` +} + +// Component defines a single Helm release component within a package source +type Component struct { + // Name is the unique identifier for this component within the package source + // +required + Name string `json:"name"` + + // Path is the path to the Helm chart directory + // +required + Path string `json:"path"` + + // Install defines installation parameters for this component + // +optional + Install *ComponentInstall `json:"install,omitempty"` + + // Libraries is a list of library names that this component depends on + // These libraries must be defined at the variant level + // +optional + Libraries []string `json:"libraries,omitempty"` + + // ValuesFiles is a list of values file names to use + // +optional + ValuesFiles []string `json:"valuesFiles,omitempty"` +} + +// PackageSourceStatus defines the observed state of PackageSource +type PackageSourceStatus struct { + // Variants is a comma-separated list of package variant names + // This field is populated by the controller based on spec.variants keys + // +optional + Variants string `json:"variants,omitempty"` + + // Conditions represents the latest available observations of a PackageSource's state + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 42cf4c4a..59f67c3d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,10 +21,62 @@ 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 *Component) DeepCopyInto(out *Component) { + *out = *in + if in.Install != nil { + in, out := &in.Install, &out.Install + *out = new(ComponentInstall) + (*in).DeepCopyInto(*out) + } + if in.Libraries != nil { + in, out := &in.Libraries, &out.Libraries + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ValuesFiles != nil { + in, out := &in.ValuesFiles, &out.ValuesFiles + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Component. +func (in *Component) DeepCopy() *Component { + if in == nil { + return nil + } + out := new(Component) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComponentInstall) DeepCopyInto(out *ComponentInstall) { + *out = *in + if in.DependsOn != nil { + in, out := &in.DependsOn, &out.DependsOn + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentInstall. +func (in *ComponentInstall) DeepCopy() *ComponentInstall { + if in == nil { + return nil + } + out := new(ComponentInstall) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CozystackResourceDefinition) DeepCopyInto(out *CozystackResourceDefinition) { *out = *in @@ -256,6 +308,297 @@ func (in *CozystackResourceDefinitionSpec) DeepCopy() *CozystackResourceDefiniti return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DependencyTarget) DeepCopyInto(out *DependencyTarget) { + *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 DependencyTarget. +func (in *DependencyTarget) DeepCopy() *DependencyTarget { + if in == nil { + return nil + } + out := new(DependencyTarget) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Library) DeepCopyInto(out *Library) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Library. +func (in *Library) DeepCopy() *Library { + if in == nil { + return nil + } + out := new(Library) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Package) DeepCopyInto(out *Package) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Package. +func (in *Package) DeepCopy() *Package { + if in == nil { + return nil + } + out := new(Package) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Package) 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 *PackageComponent) DeepCopyInto(out *PackageComponent) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + 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 PackageComponent. +func (in *PackageComponent) DeepCopy() *PackageComponent { + if in == nil { + return nil + } + out := new(PackageComponent) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackageList) DeepCopyInto(out *PackageList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Package, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageList. +func (in *PackageList) DeepCopy() *PackageList { + if in == nil { + return nil + } + out := new(PackageList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PackageList) 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 *PackageSource) DeepCopyInto(out *PackageSource) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageSource. +func (in *PackageSource) DeepCopy() *PackageSource { + if in == nil { + return nil + } + out := new(PackageSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PackageSource) 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 *PackageSourceList) DeepCopyInto(out *PackageSourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PackageSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageSourceList. +func (in *PackageSourceList) DeepCopy() *PackageSourceList { + if in == nil { + return nil + } + out := new(PackageSourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PackageSourceList) 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 *PackageSourceRef) DeepCopyInto(out *PackageSourceRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageSourceRef. +func (in *PackageSourceRef) DeepCopy() *PackageSourceRef { + if in == nil { + return nil + } + out := new(PackageSourceRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackageSourceSpec) DeepCopyInto(out *PackageSourceSpec) { + *out = *in + if in.SourceRef != nil { + in, out := &in.SourceRef, &out.SourceRef + *out = new(PackageSourceRef) + **out = **in + } + if in.Variants != nil { + in, out := &in.Variants, &out.Variants + *out = make([]Variant, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageSourceSpec. +func (in *PackageSourceSpec) DeepCopy() *PackageSourceSpec { + if in == nil { + return nil + } + out := new(PackageSourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackageSourceStatus) DeepCopyInto(out *PackageSourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageSourceStatus. +func (in *PackageSourceStatus) DeepCopy() *PackageSourceStatus { + if in == nil { + return nil + } + out := new(PackageSourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackageSpec) DeepCopyInto(out *PackageSpec) { + *out = *in + if in.IgnoreDependencies != nil { + in, out := &in.IgnoreDependencies, &out.IgnoreDependencies + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make(map[string]PackageComponent, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageSpec. +func (in *PackageSpec) DeepCopy() *PackageSpec { + if in == nil { + return nil + } + out := new(PackageSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PackageStatus) DeepCopyInto(out *PackageStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PackageStatus. +func (in *PackageStatus) DeepCopy() *PackageStatus { + if in == nil { + return nil + } + out := new(PackageStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in Selector) DeepCopyInto(out *Selector) { { @@ -292,6 +635,38 @@ func (in *SourceRef) DeepCopy() *SourceRef { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Variant) DeepCopyInto(out *Variant) { + *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([]Library, len(*in)) + copy(*out, *in) + } + if in.Components != nil { + in, out := &in.Components, &out.Components + *out = make([]Component, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Variant. +func (in *Variant) DeepCopy() *Variant { + if in == nil { + return nil + } + out := new(Variant) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Workload) DeepCopyInto(out *Workload) { *out = *in diff --git a/cmd/cozypkg/cmd/add.go b/cmd/cozypkg/cmd/add.go new file mode 100644 index 00000000..dce99020 --- /dev/null +++ b/cmd/cozypkg/cmd/add.go @@ -0,0 +1,565 @@ +/* +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 cmd + +import ( + "bufio" + "context" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/spf13/cobra" + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +var addCmdFlags struct { + files []string + kubeconfig string +} + +var addCmd = &cobra.Command{ + Use: "add [package]...", + Short: "Install PackageSource and its dependencies interactively", + Long: `Install PackageSource and its dependencies interactively. + +You can specify packages as arguments or use -f flag to read from files. +Multiple -f flags can be specified, and they can point to files or directories.`, + Args: cobra.ArbitraryArgs, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + // Collect package names from arguments and files + packageNames := make(map[string]bool) + packagesFromFiles := make(map[string]string) // packageName -> filePath + + for _, arg := range args { + packageNames[arg] = true + } + + // Read packages from files + for _, filePath := range addCmdFlags.files { + packages, err := readPackagesFromFile(filePath) + if err != nil { + return fmt.Errorf("failed to read packages from %s: %w", filePath, err) + } + for _, pkg := range packages { + packageNames[pkg] = true + packagesFromFiles[pkg] = filePath + } + } + + if len(packageNames) == 0 { + return fmt.Errorf("no packages specified") + } + + // Create Kubernetes client config + var config *rest.Config + var err error + + if addCmdFlags.kubeconfig != "" { + config, err = clientcmd.BuildConfigFromFlags("", addCmdFlags.kubeconfig) + if err != nil { + return fmt.Errorf("failed to load kubeconfig from %s: %w", addCmdFlags.kubeconfig, err) + } + } else { + config, err = ctrl.GetConfig() + if err != nil { + return fmt.Errorf("failed to get kubeconfig: %w", err) + } + } + + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(cozyv1alpha1.AddToScheme(scheme)) + + k8sClient, err := client.New(config, client.Options{Scheme: scheme}) + if err != nil { + return fmt.Errorf("failed to create k8s client: %w", err) + } + + // Process each package + for packageName := range packageNames { + // Check if package comes from a file + if filePath, fromFile := packagesFromFiles[packageName]; fromFile { + // Try to create Package directly from file + if err := createPackageFromFile(ctx, k8sClient, filePath, packageName); err == nil { + fmt.Fprintf(os.Stderr, "✓ Added Package %s\n", packageName) + continue + } + // If failed, fall back to interactive installation + } + + // Interactive installation from PackageSource + if err := installPackage(ctx, k8sClient, packageName); err != nil { + return err + } + } + + return nil + }, +} + +func readPackagesFromFile(filePath string) ([]string, error) { + info, err := os.Stat(filePath) + if err != nil { + return nil, err + } + + var packages []string + + if info.IsDir() { + // Read all YAML files from directory + err := filepath.Walk(filePath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || !strings.HasSuffix(path, ".yaml") && !strings.HasSuffix(path, ".yml") { + return nil + } + + pkgs, err := readPackagesFromYAMLFile(path) + if err != nil { + return fmt.Errorf("failed to read %s: %w", path, err) + } + packages = append(packages, pkgs...) + return nil + }) + if err != nil { + return nil, err + } + } else { + packages, err = readPackagesFromYAMLFile(filePath) + if err != nil { + return nil, err + } + } + + return packages, nil +} + +func readPackagesFromYAMLFile(filePath string) ([]string, error) { + data, err := os.ReadFile(filePath) + if err != nil { + return nil, err + } + + var packages []string + + // Split YAML documents (in case of multiple resources) + documents := strings.Split(string(data), "---") + + for _, doc := range documents { + doc = strings.TrimSpace(doc) + if doc == "" { + continue + } + + // Parse using Kubernetes decoder + decoder := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + obj := &unstructured.Unstructured{} + _, _, err := decoder.Decode([]byte(doc), nil, obj) + if err != nil { + continue + } + + // Check if it's a Package + if obj.GetKind() == "Package" { + name := obj.GetName() + if name != "" { + packages = append(packages, name) + } + continue + } + + // Check if it's a PackageSource + if obj.GetKind() == "PackageSource" { + name := obj.GetName() + if name != "" { + packages = append(packages, name) + } + continue + } + + // Try to parse as PackageList + if obj.GetKind() == "PackageList" { + items, found, err := unstructured.NestedSlice(obj.Object, "items") + if err == nil && found { + for _, item := range items { + if itemMap, ok := item.(map[string]interface{}); ok { + if metadata, ok := itemMap["metadata"].(map[string]interface{}); ok { + if name, ok := metadata["name"].(string); ok && name != "" { + packages = append(packages, name) + } + } + } + } + } + continue + } + + // Try to parse as PackageSourceList + if obj.GetKind() == "PackageSourceList" { + items, found, err := unstructured.NestedSlice(obj.Object, "items") + if err == nil && found { + for _, item := range items { + if itemMap, ok := item.(map[string]interface{}); ok { + if metadata, ok := itemMap["metadata"].(map[string]interface{}); ok { + if name, ok := metadata["name"].(string); ok && name != "" { + packages = append(packages, name) + } + } + } + } + } + continue + } + } + + if len(packages) == 0 { + return nil, fmt.Errorf("no valid packages found in file") + } + + return packages, nil +} + +// buildDependencyTree builds a dependency tree starting from the root PackageSource +// Returns both the dependency tree and a map of dependencies to their requesters +func buildDependencyTree(ctx context.Context, k8sClient client.Client, rootName string) (map[string][]string, map[string]string, error) { + tree := make(map[string][]string) + dependencyRequesters := make(map[string]string) // dep -> requester + visited := make(map[string]bool) + + // Ensure root is in tree even if it has no dependencies + tree[rootName] = []string{} + + var buildTree func(string) error + buildTree = func(pkgName string) error { + if visited[pkgName] { + return nil + } + visited[pkgName] = true + + // Get PackageSource + ps := &cozyv1alpha1.PackageSource{} + if err := k8sClient.Get(ctx, client.ObjectKey{Name: pkgName}, ps); err != nil { + // If PackageSource doesn't exist, just skip it + return nil + } + + // Collect all dependencies from all variants + deps := make(map[string]bool) + for _, variant := range ps.Spec.Variants { + for _, dep := range variant.DependsOn { + deps[dep] = true + } + } + + // Add dependencies to tree + for dep := range deps { + if _, exists := tree[pkgName]; !exists { + tree[pkgName] = []string{} + } + tree[pkgName] = append(tree[pkgName], dep) + // Track who requested this dependency + dependencyRequesters[dep] = pkgName + // Recursively build tree for dependencies + if err := buildTree(dep); err != nil { + return err + } + } + + return nil + } + + if err := buildTree(rootName); err != nil { + return nil, nil, err + } + + return tree, dependencyRequesters, nil +} + +// topologicalSort performs topological sort on the dependency tree +// Returns order from root to leaves (dependencies first) +func topologicalSort(tree map[string][]string) ([]string, error) { + // Build reverse graph (dependencies -> dependents) + reverseGraph := make(map[string][]string) + allNodes := make(map[string]bool) + + for node, deps := range tree { + allNodes[node] = true + for _, dep := range deps { + allNodes[dep] = true + reverseGraph[dep] = append(reverseGraph[dep], node) + } + } + + // Calculate in-degrees (how many dependencies a node has) + inDegree := make(map[string]int) + for node := range allNodes { + inDegree[node] = 0 + } + for node, deps := range tree { + inDegree[node] = len(deps) + } + + // Kahn's algorithm - start with nodes that have no dependencies + var queue []string + for node, degree := range inDegree { + if degree == 0 { + queue = append(queue, node) + } + } + + var result []string + for len(queue) > 0 { + node := queue[0] + queue = queue[1:] + result = append(result, node) + + // Process dependents (nodes that depend on this node) + for _, dependent := range reverseGraph[node] { + inDegree[dependent]-- + if inDegree[dependent] == 0 { + queue = append(queue, dependent) + } + } + } + + // Check for cycles + if len(result) != len(allNodes) { + return nil, fmt.Errorf("dependency cycle detected") + } + + return result, nil +} + +// createPackageFromFile creates a Package resource directly from a YAML file +func createPackageFromFile(ctx context.Context, k8sClient client.Client, filePath string, packageName string) error { + data, err := os.ReadFile(filePath) + if err != nil { + return err + } + + // Split YAML documents + documents := strings.Split(string(data), "---") + + for _, doc := range documents { + doc = strings.TrimSpace(doc) + if doc == "" { + continue + } + + // Parse using Kubernetes decoder + decoder := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + obj := &unstructured.Unstructured{} + _, _, err := decoder.Decode([]byte(doc), nil, obj) + if err != nil { + continue + } + + // Check if it's a Package with matching name + if obj.GetKind() == "Package" && obj.GetName() == packageName { + // Convert to Package + var pkg cozyv1alpha1.Package + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &pkg); err != nil { + return fmt.Errorf("failed to convert Package: %w", err) + } + + // Create Package + if err := k8sClient.Create(ctx, &pkg); err != nil { + return fmt.Errorf("failed to create Package: %w", err) + } + + return nil + } + } + + return fmt.Errorf("Package %s not found in file", packageName) +} + +func installPackage(ctx context.Context, k8sClient client.Client, packageSourceName string) error { + // Get PackageSource + packageSource := &cozyv1alpha1.PackageSource{} + if err := k8sClient.Get(ctx, client.ObjectKey{Name: packageSourceName}, packageSource); err != nil { + return fmt.Errorf("failed to get PackageSource %s: %w", packageSourceName, err) + } + + // Build dependency tree + dependencyTree, dependencyRequesters, err := buildDependencyTree(ctx, k8sClient, packageSourceName) + if err != nil { + return fmt.Errorf("failed to build dependency tree: %w", err) + } + + // Topological sort (install from root to leaves) + installOrder, err := topologicalSort(dependencyTree) + if err != nil { + return fmt.Errorf("failed to sort dependencies: %w", err) + } + + // Get all PackageSources for variant selection + var allPackageSources cozyv1alpha1.PackageSourceList + if err := k8sClient.List(ctx, &allPackageSources); err != nil { + return fmt.Errorf("failed to list PackageSources: %w", err) + } + + packageSourceMap := make(map[string]*cozyv1alpha1.PackageSource) + for i := range allPackageSources.Items { + packageSourceMap[allPackageSources.Items[i].Name] = &allPackageSources.Items[i] + } + + // Get all installed Packages + var installedPackages cozyv1alpha1.PackageList + if err := k8sClient.List(ctx, &installedPackages); err != nil { + return fmt.Errorf("failed to list Packages: %w", err) + } + + installedMap := make(map[string]*cozyv1alpha1.Package) + for i := range installedPackages.Items { + installedMap[installedPackages.Items[i].Name] = &installedPackages.Items[i] + } + + // First, collect all variant selections + fmt.Fprintf(os.Stderr, "Installing %s and its dependencies...\n\n", packageSourceName) + packageVariants := make(map[string]string) // packageName -> variant + + for _, pkgName := range installOrder { + // Check if already installed + if installed, exists := installedMap[pkgName]; exists { + variant := installed.Spec.Variant + if variant == "" { + variant = "default" + } + fmt.Fprintf(os.Stderr, "✓ %s (already installed, variant: %s)\n", pkgName, variant) + packageVariants[pkgName] = variant + continue + } + + // Get PackageSource for this dependency + ps, exists := packageSourceMap[pkgName] + if !exists { + requester := dependencyRequesters[pkgName] + if requester != "" { + return fmt.Errorf("PackageSource %s not found (required by %s)", pkgName, requester) + } + return fmt.Errorf("PackageSource %s not found", pkgName) + } + + // Select variant interactively + variant, err := selectVariantInteractive(ps) + if err != nil { + return fmt.Errorf("failed to select variant for %s: %w", pkgName, err) + } + + packageVariants[pkgName] = variant + } + + // Now create all Package resources + for _, pkgName := range installOrder { + // Skip if already installed + if _, exists := installedMap[pkgName]; exists { + continue + } + + variant := packageVariants[pkgName] + + // Create Package + pkg := &cozyv1alpha1.Package{ + ObjectMeta: metav1.ObjectMeta{ + Name: pkgName, + }, + Spec: cozyv1alpha1.PackageSpec{ + Variant: variant, + }, + } + + if err := k8sClient.Create(ctx, pkg); err != nil { + return fmt.Errorf("failed to create Package %s: %w", pkgName, err) + } + + fmt.Fprintf(os.Stderr, "✓ Added Package %s\n", pkgName) + } + + return nil +} + +// selectVariantInteractive prompts user to select a variant +func selectVariantInteractive(ps *cozyv1alpha1.PackageSource) (string, error) { + if len(ps.Spec.Variants) == 0 { + return "", fmt.Errorf("no variants available for PackageSource %s", ps.Name) + } + + reader := bufio.NewReader(os.Stdin) + + fmt.Fprintf(os.Stderr, "\nPackageSource: %s\n", ps.Name) + fmt.Fprintf(os.Stderr, "Available variants:\n") + for i, variant := range ps.Spec.Variants { + fmt.Fprintf(os.Stderr, " %d. %s\n", i+1, variant.Name) + } + + // If only one variant, use it as default + defaultVariant := ps.Spec.Variants[0].Name + var prompt string + if len(ps.Spec.Variants) == 1 { + prompt = "Select variant [1]: " + } else { + prompt = fmt.Sprintf("Select variant (1-%d): ", len(ps.Spec.Variants)) + } + + for { + fmt.Fprintf(os.Stderr, prompt) + input, err := reader.ReadString('\n') + if err != nil { + return "", fmt.Errorf("failed to read input: %w", err) + } + + input = strings.TrimSpace(input) + + // If input is empty and there's a default variant, use it + if input == "" && len(ps.Spec.Variants) == 1 { + return defaultVariant, nil + } + + choice, err := strconv.Atoi(input) + if err != nil || choice < 1 || choice > len(ps.Spec.Variants) { + fmt.Fprintf(os.Stderr, "Invalid choice. Please enter a number between 1 and %d.\n", len(ps.Spec.Variants)) + continue + } + + return ps.Spec.Variants[choice-1].Name, nil + } +} + +func init() { + rootCmd.AddCommand(addCmd) + addCmd.Flags().StringArrayVarP(&addCmdFlags.files, "file", "f", []string{}, "Read packages from file or directory (can be specified multiple times)") + addCmd.Flags().StringVar(&addCmdFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file (defaults to ~/.kube/config or KUBECONFIG env var)") +} + diff --git a/cmd/cozypkg/cmd/del.go b/cmd/cozypkg/cmd/del.go new file mode 100644 index 00000000..ed4a74c2 --- /dev/null +++ b/cmd/cozypkg/cmd/del.go @@ -0,0 +1,121 @@ +/* +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 cmd + +import ( + "context" + "fmt" + "os" + + "github.com/spf13/cobra" + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +var delCmdFlags struct { + files []string + kubeconfig string +} + +var delCmd = &cobra.Command{ + Use: "del [package]...", + Short: "Delete Package resources", + Long: `Delete Package resources. + +You can specify packages as arguments or use -f flag to read from files. +Multiple -f flags can be specified, and they can point to files or directories.`, + Args: cobra.ArbitraryArgs, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + // Collect package names from arguments and files + packageNames := make(map[string]bool) + for _, arg := range args { + packageNames[arg] = true + } + + // Read packages from files (reuse function from add.go) + for _, filePath := range delCmdFlags.files { + packages, err := readPackagesFromFile(filePath) + if err != nil { + return fmt.Errorf("failed to read packages from %s: %w", filePath, err) + } + for _, pkg := range packages { + packageNames[pkg] = true + } + } + + if len(packageNames) == 0 { + return fmt.Errorf("no packages specified") + } + + // Create Kubernetes client config + var config *rest.Config + var err error + + if delCmdFlags.kubeconfig != "" { + config, err = clientcmd.BuildConfigFromFlags("", delCmdFlags.kubeconfig) + if err != nil { + return fmt.Errorf("failed to load kubeconfig from %s: %w", delCmdFlags.kubeconfig, err) + } + } else { + config, err = ctrl.GetConfig() + if err != nil { + return fmt.Errorf("failed to get kubeconfig: %w", err) + } + } + + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(cozyv1alpha1.AddToScheme(scheme)) + + k8sClient, err := client.New(config, client.Options{Scheme: scheme}) + if err != nil { + return fmt.Errorf("failed to create k8s client: %w", err) + } + + // Delete each package + for packageName := range packageNames { + pkg := &cozyv1alpha1.Package{} + pkg.Name = packageName + if err := k8sClient.Delete(ctx, pkg); err != nil { + if client.IgnoreNotFound(err) == nil { + fmt.Fprintf(os.Stderr, "⚠ Package %s not found, skipping\n", packageName) + continue + } + return fmt.Errorf("failed to delete Package %s: %w", packageName, err) + } + fmt.Fprintf(os.Stderr, "✓ Deleted Package %s\n", packageName) + } + + return nil + }, +} + +func init() { + rootCmd.AddCommand(delCmd) + delCmd.Flags().StringArrayVarP(&delCmdFlags.files, "file", "f", []string{}, "Read packages from file or directory (can be specified multiple times)") + delCmd.Flags().StringVar(&delCmdFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file (defaults to ~/.kube/config or KUBECONFIG env var)") +} + diff --git a/cmd/cozypkg/cmd/dependencies.go b/cmd/cozypkg/cmd/dependencies.go new file mode 100644 index 00000000..b7a8c17b --- /dev/null +++ b/cmd/cozypkg/cmd/dependencies.go @@ -0,0 +1,300 @@ +/* +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 cmd + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/emicklei/dot" + "github.com/spf13/cobra" + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +var dotCmdFlags struct { + installed bool + components bool + files []string + kubeconfig string +} + +var dotCmd = &cobra.Command{ + Use: "dot [package]...", + Short: "Generate dependency graph as graphviz DOT format", + Long: `Generate dependency graph as graphviz DOT format. + +Pipe the output through the "dot" program (part of graphviz package) to render the graph: + + cozypkg dot | dot -Tpng > graph.png + +By default, shows dependencies for all PackageSource resources. +Use --installed to show only installed Package resources. +Specify packages as arguments or use -f flag to read from files.`, + Args: cobra.ArbitraryArgs, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + // Collect package names from arguments and files + packageNames := make(map[string]bool) + for _, arg := range args { + packageNames[arg] = true + } + + // Read packages from files (reuse function from add.go) + for _, filePath := range dotCmdFlags.files { + packages, err := readPackagesFromFile(filePath) + if err != nil { + return fmt.Errorf("failed to read packages from %s: %w", filePath, err) + } + for _, pkg := range packages { + packageNames[pkg] = true + } + } + + // Convert to slice, empty means all packages + var selectedPackages []string + if len(packageNames) > 0 { + for pkg := range packageNames { + selectedPackages = append(selectedPackages, pkg) + } + } + + // If multiple packages specified, show graph for all of them + // If single package, use packageName for backward compatibility + var packageName string + if len(selectedPackages) == 1 { + packageName = selectedPackages[0] + } else if len(selectedPackages) > 1 { + // Multiple packages - pass empty string to packageName, use selectedPackages + packageName = "" + } + + // packagesOnly is inverse of components flag (if components=false, then packagesOnly=true) + packagesOnly := !dotCmdFlags.components + graph, allNodes, err := buildGraphFromCluster(ctx, dotCmdFlags.kubeconfig, packagesOnly, dotCmdFlags.installed, packageName, selectedPackages) + if err != nil { + return fmt.Errorf("error getting PackageSource dependencies: %w", err) + } + + dotGraph := generateDOTGraph(graph, allNodes, packagesOnly) + dotGraph.Write(os.Stdout) + + return nil + }, +} + +func init() { + rootCmd.AddCommand(dotCmd) + dotCmd.Flags().BoolVarP(&dotCmdFlags.installed, "installed", "i", false, "show dependencies only for installed Package resources") + dotCmd.Flags().BoolVar(&dotCmdFlags.components, "components", true, "show component-level dependencies (default: true)") + dotCmd.Flags().StringArrayVarP(&dotCmdFlags.files, "file", "f", []string{}, "Read packages from file or directory (can be specified multiple times)") + dotCmd.Flags().StringVar(&dotCmdFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file (defaults to ~/.kube/config or KUBECONFIG env var)") +} + +var ( + dependenciesScheme = runtime.NewScheme() +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(dependenciesScheme)) + utilruntime.Must(cozyv1alpha1.AddToScheme(dependenciesScheme)) +} + +// buildGraphFromCluster builds a dependency graph from PackageSource resources in the cluster. +func buildGraphFromCluster(ctx context.Context, kubeconfig string, packagesOnly bool, installedOnly bool, packageName string, selectedPackages []string) (map[string][]string, map[string]bool, error) { + // Create Kubernetes client config + var config *rest.Config + var err error + + if kubeconfig != "" { + // Load kubeconfig from explicit path + config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + return nil, nil, fmt.Errorf("failed to load kubeconfig from %s: %w", kubeconfig, err) + } + } else { + // Use default kubeconfig loading (from env var or ~/.kube/config) + config, err = ctrl.GetConfig() + if err != nil { + return nil, nil, fmt.Errorf("failed to get kubeconfig: %w", err) + } + } + + k8sClient, err := client.New(config, client.Options{Scheme: dependenciesScheme}) + if err != nil { + return nil, nil, fmt.Errorf("failed to create k8s client: %w", err) + } + + // Get installed Packages if needed + installedPackages := make(map[string]bool) + if installedOnly || packageName != "" { + var packageList cozyv1alpha1.PackageList + if err := k8sClient.List(ctx, &packageList); err != nil { + return nil, nil, fmt.Errorf("failed to list Packages: %w", err) + } + for _, pkg := range packageList.Items { + installedPackages[pkg.Name] = true + } + } + + // List all PackageSource resources + var packageSourceList cozyv1alpha1.PackageSourceList + if err := k8sClient.List(ctx, &packageSourceList); err != nil { + return nil, nil, fmt.Errorf("failed to list PackageSources: %w", err) + } + + graph := make(map[string][]string) + allNodes := make(map[string]bool) + + // Process each PackageSource + for _, ps := range packageSourceList.Items { + psName := ps.Name + if psName == "" { + continue + } + + // Filter by package name if specified + if packageName != "" && psName != packageName { + continue + } + + // Filter by selected packages if specified + if len(selectedPackages) > 0 { + found := false + for _, selected := range selectedPackages { + if psName == selected { + found = true + break + } + } + if !found { + continue + } + } + + // Filter by installed packages if flag is set + if installedOnly && !installedPackages[psName] { + continue + } + + allNodes[psName] = true + + // Extract dependencies from variants + for _, variant := range ps.Spec.Variants { + // Variant-level dependencies + for _, dep := range variant.DependsOn { + // If installedOnly is set, only include dependencies that are installed + if installedOnly && !installedPackages[dep] { + continue + } + graph[psName] = append(graph[psName], dep) + allNodes[dep] = true + } + + // Component-level dependencies + if !packagesOnly { + for _, component := range variant.Components { + componentName := fmt.Sprintf("%s.%s", psName, component.Name) + allNodes[componentName] = true + + if component.Install != nil { + for _, dep := range component.Install.DependsOn { + // Check if it's a local component dependency or external + if strings.Contains(dep, ".") { + graph[componentName] = append(graph[componentName], dep) + allNodes[dep] = true + } else { + // Local component dependency + localDep := fmt.Sprintf("%s.%s", psName, dep) + graph[componentName] = append(graph[componentName], localDep) + allNodes[localDep] = true + } + } + } + } + } + } + } + + return graph, allNodes, nil +} + +// generateDOTGraph generates a DOT graph from the dependency graph. +func generateDOTGraph(graph map[string][]string, allNodes map[string]bool, packagesOnly bool) *dot.Graph { + g := dot.NewGraph(dot.Directed) + g.Attr("rankdir", "LR") + g.Attr("nodesep", "0.5") + g.Attr("ranksep", "1.0") + + // Add nodes + for node := range allNodes { + if packagesOnly && strings.Contains(node, ".") && !strings.HasPrefix(node, "cozystack.") { + // Skip component nodes when packages-only is enabled + continue + } + + n := g.Node(node) + + // Style nodes based on type + if strings.Contains(node, ".") && !strings.HasPrefix(node, "cozystack.") { + // Component node + n.Attr("shape", "box") + n.Attr("style", "rounded,filled") + n.Attr("fillcolor", "lightyellow") + n.Attr("label", strings.Split(node, ".")[len(strings.Split(node, "."))-1]) + } else { + // Package node + n.Attr("shape", "box") + n.Attr("style", "rounded,filled") + n.Attr("fillcolor", "lightblue") + n.Attr("label", node) + } + } + + // Add edges + for source, targets := range graph { + if packagesOnly && strings.Contains(source, ".") && !strings.HasPrefix(source, "cozystack.") { + // Skip component edges when packages-only is enabled + continue + } + + for _, target := range targets { + if packagesOnly && strings.Contains(target, ".") && !strings.HasPrefix(target, "cozystack.") { + // Skip component edges when packages-only is enabled + continue + } + + // Only add edge if both nodes exist + if allNodes[source] && allNodes[target] { + g.Edge(g.Node(source), g.Node(target)) + } + } + } + + return g +} + diff --git a/cmd/cozypkg/cmd/list.go b/cmd/cozypkg/cmd/list.go new file mode 100644 index 00000000..faa898b1 --- /dev/null +++ b/cmd/cozypkg/cmd/list.go @@ -0,0 +1,201 @@ +/* +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 cmd + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + _ "k8s.io/client-go/plugin/pkg/client/auth" +) + +var listCmdFlags struct { + installed bool + components bool + kubeconfig string +} + +var listCmd = &cobra.Command{ + Use: "list", + Short: "List PackageSource or Package resources", + Long: `List PackageSource or Package resources in table format. + +By default, lists PackageSource resources. Use --installed flag to list installed Package resources. +Use --components flag to show components on separate lines.`, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + // Create Kubernetes client config + var config *rest.Config + var err error + + if listCmdFlags.kubeconfig != "" { + config, err = clientcmd.BuildConfigFromFlags("", listCmdFlags.kubeconfig) + if err != nil { + return fmt.Errorf("failed to load kubeconfig from %s: %w", listCmdFlags.kubeconfig, err) + } + } else { + config, err = ctrl.GetConfig() + if err != nil { + return fmt.Errorf("failed to get kubeconfig: %w", err) + } + } + + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(cozyv1alpha1.AddToScheme(scheme)) + + k8sClient, err := client.New(config, client.Options{Scheme: scheme}) + if err != nil { + return fmt.Errorf("failed to create k8s client: %w", err) + } + + if listCmdFlags.installed { + return listPackages(ctx, k8sClient, listCmdFlags.components) + } + return listPackageSources(ctx, k8sClient, listCmdFlags.components) + }, +} + +func listPackageSources(ctx context.Context, k8sClient client.Client, showComponents bool) error { + var psList cozyv1alpha1.PackageSourceList + if err := k8sClient.List(ctx, &psList); err != nil { + return fmt.Errorf("failed to list PackageSources: %w", err) + } + + // Print header + fmt.Fprintf(os.Stdout, "%-50s %-30s %-10s %s\n", "NAME", "VARIANTS", "READY", "STATUS") + fmt.Fprintf(os.Stdout, "%-50s %-30s %-10s %s\n", strings.Repeat("-", 50), strings.Repeat("-", 30), strings.Repeat("-", 10), strings.Repeat("-", 50)) + + // Print rows + for _, ps := range psList.Items { + // Get variants + var variants []string + for _, variant := range ps.Spec.Variants { + variants = append(variants, variant.Name) + } + variantsStr := strings.Join(variants, ",") + if len(variantsStr) > 28 { + variantsStr = variantsStr[:25] + "..." + } + + // Get Ready condition + ready := "Unknown" + status := "" + for _, condition := range ps.Status.Conditions { + if condition.Type == "Ready" { + ready = string(condition.Status) + status = condition.Message + if len(status) > 48 { + status = status[:45] + "..." + } + break + } + } + + fmt.Fprintf(os.Stdout, "%-50s %-30s %-10s %s\n", ps.Name, variantsStr, ready, status) + + // Show components if requested + if showComponents { + for _, variant := range ps.Spec.Variants { + for _, component := range variant.Components { + fmt.Fprintf(os.Stdout, " %-48s %-30s %-10s %s\n", + fmt.Sprintf("%s.%s", ps.Name, component.Name), + variant.Name, "", "") + } + } + } + } + + return nil +} + +func listPackages(ctx context.Context, k8sClient client.Client, showComponents bool) error { + var pkgList cozyv1alpha1.PackageList + if err := k8sClient.List(ctx, &pkgList); err != nil { + return fmt.Errorf("failed to list Packages: %w", err) + } + + // Print header + fmt.Fprintf(os.Stdout, "%-50s %-20s %-10s %s\n", "NAME", "VARIANT", "READY", "STATUS") + fmt.Fprintf(os.Stdout, "%-50s %-20s %-10s %s\n", strings.Repeat("-", 50), strings.Repeat("-", 20), strings.Repeat("-", 10), strings.Repeat("-", 50)) + + // Print rows + for _, pkg := range pkgList.Items { + variant := pkg.Spec.Variant + if variant == "" { + variant = "default" + } + + // Get Ready condition + ready := "Unknown" + status := "" + for _, condition := range pkg.Status.Conditions { + if condition.Type == "Ready" { + ready = string(condition.Status) + status = condition.Message + if len(status) > 48 { + status = status[:45] + "..." + } + break + } + } + + fmt.Fprintf(os.Stdout, "%-50s %-20s %-10s %s\n", pkg.Name, variant, ready, status) + + // Show components if requested + if showComponents { + // Get PackageSource to show components + ps := &cozyv1alpha1.PackageSource{} + if err := k8sClient.Get(ctx, client.ObjectKey{Name: pkg.Name}, ps); err == nil { + // Find the variant + for _, v := range ps.Spec.Variants { + if v.Name == variant { + for _, component := range v.Components { + fmt.Fprintf(os.Stdout, " %-48s %-20s %-10s %s\n", + fmt.Sprintf("%s.%s", pkg.Name, component.Name), + variant, "", "") + } + break + } + } + } + } + } + + return nil +} + +func init() { + rootCmd.AddCommand(listCmd) + listCmd.Flags().BoolVarP(&listCmdFlags.installed, "installed", "i", false, "list installed Package resources instead of PackageSource resources") + listCmd.Flags().BoolVar(&listCmdFlags.components, "components", false, "show components on separate lines") + listCmd.Flags().StringVar(&listCmdFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file (defaults to ~/.kube/config or KUBECONFIG env var)") +} + diff --git a/cmd/cozypkg/cmd/root.go b/cmd/cozypkg/cmd/root.go new file mode 100644 index 00000000..62bd54aa --- /dev/null +++ b/cmd/cozypkg/cmd/root.go @@ -0,0 +1,49 @@ +/* +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 cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +// rootCmd represents the base command when called without any subcommands. +var rootCmd = &cobra.Command{ + Use: "cozypkg", + Short: "A CLI for managing Cozystack packages", + Long: ``, + SilenceErrors: true, + SilenceUsage: true, + DisableAutoGenTag: true, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() error { + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + return err + } + return nil +} + +func init() { + // Commands are registered in their respective init() functions +} + diff --git a/cmd/cozypkg/main.go b/cmd/cozypkg/main.go new file mode 100644 index 00000000..7f6b9ead --- /dev/null +++ b/cmd/cozypkg/main.go @@ -0,0 +1,30 @@ +/* +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 ( + "os" + + "github.com/cozystack/cozystack/cmd/cozypkg/cmd" +) + +func main() { + if err := cmd.Execute(); err != nil { + os.Exit(1) + } +} + diff --git a/cmd/cozystack-operator/main.go b/cmd/cozystack-operator/main.go new file mode 100644 index 00000000..4c182735 --- /dev/null +++ b/cmd/cozystack-operator/main.go @@ -0,0 +1,481 @@ +/* +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" + "net/url" + "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" + // +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 cozystackVersion string + var installFluxResources stringSliceFlag + var platformSource string + var platformSourceName string + + 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.StringVar(&cozystackVersion, "cozystack-version", "unknown", + "Version of Cozystack") + flag.StringVar(&platformSource, "platform-source", "", "Platform source URL (oci:// or git://). If specified, generates OCIRepository or GitRepository resource.") + flag.StringVar(&platformSourceName, "platform-source-name", "cozystack-packages", "Name for the generated platform source resource (default: cozystack-packages)") + + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + 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: "cozystack-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") + } + } + + // Generate and install platform source resource if specified + if platformSource != "" { + setupLog.Info("Generating platform source resource", "source", platformSource, "name", platformSourceName) + installCtx, installCancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer installCancel() + + if err := installPlatformSourceResource(installCtx, mgr.GetClient(), platformSource, platformSourceName); err != nil { + setupLog.Error(err, "failed to install platform source resource") + os.Exit(1) + } else { + setupLog.Info("Platform source resource installation completed successfully") + } + } + + // Setup PackageSource reconciler + if err := (&operator.PackageSourceReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "PackageSource") + os.Exit(1) + } + + // Setup Package reconciler + if err := (&operator.PackageReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Package") + 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) + } + + 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 +} + +// installPlatformSourceResource generates and installs a Flux source resource (OCIRepository or GitRepository) +// based on the platform source URL +func installPlatformSourceResource(ctx context.Context, k8sClient client.Client, sourceURL, resourceName string) error { + logger := log.FromContext(ctx) + + // Parse the source URL to determine type + sourceType, repoURL, ref, err := parsePlatformSource(sourceURL) + if err != nil { + return fmt.Errorf("failed to parse platform source URL: %w", err) + } + + var obj *unstructured.Unstructured + switch sourceType { + case "oci": + obj, err = generateOCIRepository(resourceName, repoURL, ref) + if err != nil { + return fmt.Errorf("failed to generate OCIRepository: %w", err) + } + case "git": + obj, err = generateGitRepository(resourceName, repoURL, ref) + if err != nil { + return fmt.Errorf("failed to generate GitRepository: %w", err) + } + default: + return fmt.Errorf("unsupported source type: %s (expected oci:// or git://)", sourceType) + } + + // Apply the resource (create or update) + logger.Info("Applying platform source resource", + "apiVersion", obj.GetAPIVersion(), + "kind", obj.GetKind(), + "name", obj.GetName(), + "namespace", obj.GetNamespace(), + ) + + 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 platform source 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 platform source resource", "kind", obj.GetKind(), "name", obj.GetName()) + } + + return nil +} + +// parsePlatformSource parses the source URL and returns the type, repository URL, and reference +// Supports formats: +// - oci://registry.example.com/repo@sha256:digest +// - oci://registry.example.com/repo (ref will be empty) +// - git://github.com/user/repo@branch +// - git://github.com/user/repo (ref will default to "main") +// - https://github.com/user/repo@branch (treated as git) +func parsePlatformSource(sourceURL string) (sourceType, repoURL, ref string, err error) { + // Normalize the URL by trimming whitespace + sourceURL = strings.TrimSpace(sourceURL) + + // Check for oci:// prefix + if strings.HasPrefix(sourceURL, "oci://") { + // Remove oci:// prefix + rest := strings.TrimPrefix(sourceURL, "oci://") + + // Check for @sha256: digest (look for @ followed by sha256:) + // We need to find the last @ before sha256: to handle paths with @ symbols + sha256Idx := strings.Index(rest, "@sha256:") + if sha256Idx != -1 { + repoURL = "oci://" + rest[:sha256Idx] + ref = rest[sha256Idx+1:] // sha256:digest + } else { + // Check for @ without sha256: (might be a tag) + if atIdx := strings.LastIndex(rest, "@"); atIdx != -1 { + // Could be a tag, but for OCI we expect sha256: digest + // For now, treat everything after @ as the ref + repoURL = "oci://" + rest[:atIdx] + ref = rest[atIdx+1:] + } else { + repoURL = "oci://" + rest + ref = "" // No digest specified + } + } + return "oci", repoURL, ref, nil + } + + // Check for git:// prefix or treat as git for http/https + if strings.HasPrefix(sourceURL, "git://") || strings.HasPrefix(sourceURL, "http://") || strings.HasPrefix(sourceURL, "https://") || strings.HasPrefix(sourceURL, "ssh://") { + // Parse URL to extract ref if present + parsedURL, err := url.Parse(sourceURL) + if err != nil { + return "", "", "", fmt.Errorf("invalid URL: %w", err) + } + + // Check for @ref in the path (e.g., git://host/path@branch) + path := parsedURL.Path + if idx := strings.LastIndex(path, "@"); idx != -1 { + repoURL = fmt.Sprintf("%s://%s%s", parsedURL.Scheme, parsedURL.Host, path[:idx]) + if parsedURL.RawQuery != "" { + repoURL += "?" + parsedURL.RawQuery + } + ref = path[idx+1:] + } else { + // Default to main branch if no ref specified + repoURL = sourceURL + ref = "main" + } + + // Normalize git:// to https:// for GitRepository + if strings.HasPrefix(repoURL, "git://") { + repoURL = strings.Replace(repoURL, "git://", "https://", 1) + } + + return "git", repoURL, ref, nil + } + + return "", "", "", fmt.Errorf("unsupported source URL scheme (expected oci:// or git://): %s", sourceURL) +} + +// generateOCIRepository creates an OCIRepository resource +func generateOCIRepository(name, repoURL, digest string) (*unstructured.Unstructured, error) { + obj := &unstructured.Unstructured{} + obj.SetAPIVersion("source.toolkit.fluxcd.io/v1") + obj.SetKind("OCIRepository") + obj.SetName(name) + obj.SetNamespace("cozy-system") + + spec := map[string]interface{}{ + "interval": "5m0s", + "url": repoURL, + } + + if digest != "" { + // Ensure digest starts with sha256: + if !strings.HasPrefix(digest, "sha256:") { + digest = "sha256:" + digest + } + spec["ref"] = map[string]interface{}{ + "digest": digest, + } + } + + if err := unstructured.SetNestedField(obj.Object, spec, "spec"); err != nil { + return nil, fmt.Errorf("failed to set spec: %w", err) + } + + return obj, nil +} + +// generateGitRepository creates a GitRepository resource +func generateGitRepository(name, repoURL, ref string) (*unstructured.Unstructured, error) { + obj := &unstructured.Unstructured{} + obj.SetAPIVersion("source.toolkit.fluxcd.io/v1") + obj.SetKind("GitRepository") + obj.SetName(name) + obj.SetNamespace("cozy-system") + + spec := map[string]interface{}{ + "interval": "5m0s", + "url": repoURL, + "ref": map[string]interface{}{ + "branch": ref, + }, + } + + if err := unstructured.SetNestedField(obj.Object, spec, "spec"); err != nil { + return nil, fmt.Errorf("failed to set spec: %w", err) + } + + return obj, nil +} diff --git a/go.mod b/go.mod index 6ae20909..d03d9aff 100644 --- a/go.mod +++ b/go.mod @@ -2,38 +2,41 @@ 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/go-logr/logr v1.4.2 + github.com/emicklei/dot v1.10.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/prometheus/client_golang v1.19.1 + 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/robfig/cron/v3 v3.0.1 - github.com/spf13/cobra v1.8.1 - github.com/stretchr/testify v1.9.0 + github.com/spf13/cobra v1.9.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 + gopkg.in/yaml.v3 v3.0.1 + 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 @@ -41,85 +44,90 @@ 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/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-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_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 - 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 ) // See: issues.k8s.io/135537 -replace k8s.io/apimachinery => github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614 +replace k8s.io/apimachinery => github.com/cozystack/apimachinery v0.0.0-20251219010959-1f91eabae46c diff --git a/go.sum b/go.sum index 88c81e7b..6c466a99 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,47 +18,52 @@ 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/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614 h1:jH9elECUvhiIs3IMv3oS5k1JgCLVsSK6oU4dmq5gyW8= -github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -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/cozystack/apimachinery v0.0.0-20251219010959-1f91eabae46c h1:C2wIfH/OzhU9XOK/e6Ik9cg7nZ1z6fN4lf6a3yFdik8= +github.com/cozystack/apimachinery v0.0.0-20251219010959-1f91eabae46c/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +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/dot v1.10.0 h1:z17n0ce/FBMz3QbShSzVGhiW447Qhu7fljzvp3Gs6ig= +github.com/emicklei/dot v1.10.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +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= @@ -66,164 +71,171 @@ 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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -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= @@ -232,50 +244,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= @@ -285,37 +295,42 @@ 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/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/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/hack/update-codegen.sh b/hack/update-codegen.sh index 0e43f717..4c2f8786 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -24,6 +24,8 @@ API_KNOWN_VIOLATIONS_DIR="${API_KNOWN_VIOLATIONS_DIR:-"${SCRIPT_ROOT}/api/api-ru UPDATE_API_KNOWN_VIOLATIONS="${UPDATE_API_KNOWN_VIOLATIONS:-true}" CONTROLLER_GEN="go run sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.4" TMPDIR=$(mktemp -d) +OPERATOR_CRDDIR=packages/core/installer/crds +BACKUPSTRATEGY_CRDDIR=packages/system/backupstrategy-controller/definitions COZY_CONTROLLER_CRDDIR=packages/system/cozystack-controller/crds COZY_RD_CRDDIR=packages/system/cozystack-resource-definition-crd/definition BACKUPS_CORE_CRDDIR=packages/system/backup-controller/definitions @@ -62,6 +64,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=${TMPDIR} +mv ${TMPDIR}/cozystack.io_packages.yaml ${OPERATOR_CRDDIR}/cozystack.io_packages.yaml +mv ${TMPDIR}/cozystack.io_packagesources.yaml ${OPERATOR_CRDDIR}/cozystack.io_packagesources.yaml + mv ${TMPDIR}/cozystack.io_cozystackresourcedefinitions.yaml \ ${COZY_RD_CRDDIR}/cozystack.io_cozystackresourcedefinitions.yaml diff --git a/internal/controller/cozystackresource_controller.go b/internal/controller/cozystackresource_controller.go index 46884418..21585c6f 100644 --- a/internal/controller/cozystackresource_controller.go +++ b/internal/controller/cozystackresource_controller.go @@ -5,11 +5,13 @@ import ( "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" @@ -37,6 +39,11 @@ type CozystackResourceDefinitionReconciler struct { } func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + if err := r.reconcileCozyRDAndUpdateHelmReleases(ctx); err != nil { + return ctrl.Result{}, err + } + + // Continue with debounced restart logic return r.debouncedRestart(ctx) } @@ -185,3 +192,138 @@ func sortCozyRDs(a, b cozyv1alpha1.CozystackResourceDefinition) int { } return 1 } + +// reconcileCozyRDAndUpdateHelmReleases reconciles all CozystackResourceDefinitions and updates HelmReleases from them +func (r *CozystackResourceDefinitionReconciler) reconcileCozyRDAndUpdateHelmReleases(ctx context.Context) error { + logger := log.FromContext(ctx) + + // List all CozystackResourceDefinitions + crdList := &cozyv1alpha1.CozystackResourceDefinitionList{} + if err := r.List(ctx, crdList); err != nil { + logger.Error(err, "failed to list CozystackResourceDefinitions") + return err + } + + // Update HelmReleases for each CRD + for i := range crdList.Items { + crd := &crdList.Items[i] + 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 + } + } + + return nil +} + +// updateHelmReleasesForCRD updates all HelmReleases that match the application labels from CozystackResourceDefinition +func (r *CozystackResourceDefinitionReconciler) updateHelmReleasesForCRD(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) 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 + + // Validate that applicationKind is non-empty + if applicationKind == "" { + logger.Error(fmt.Errorf("Application.Kind is empty"), "Skipping HelmRelease update: invalid CozystackResourceDefinition", "crd", crd.Name) + return nil + } + + 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.V(4).Info("Found HelmReleases to update", "crd", crd.Name, "kind", applicationKind, "count", len(hrList.Items)) + + // 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 in HelmRelease based on CozystackResourceDefinition +func (r *CozystackResourceDefinitionReconciler) updateHelmReleaseChart(ctx context.Context, hr *helmv2.HelmRelease, crd *cozyv1alpha1.CozystackResourceDefinition) error { + logger := log.FromContext(ctx) + hrCopy := hr.DeepCopy() + updated := false + + // Validate Chart configuration exists + if crd.Spec.Release.Chart.Name == "" { + logger.V(4).Info("Skipping HelmRelease chart update: Chart.Name is empty", "crd", crd.Name) + return nil + } + + // Validate SourceRef fields + if crd.Spec.Release.Chart.SourceRef.Kind == "" || + crd.Spec.Release.Chart.SourceRef.Name == "" || + crd.Spec.Release.Chart.SourceRef.Namespace == "" { + logger.Error(fmt.Errorf("invalid SourceRef in CRD"), "Skipping HelmRelease chart update: SourceRef fields are incomplete", + "crd", crd.Name, + "kind", crd.Spec.Release.Chart.SourceRef.Kind, + "name", crd.Spec.Release.Chart.SourceRef.Name, + "namespace", crd.Spec.Release.Chart.SourceRef.Namespace) + return nil + } + + // Get version and reconcileStrategy from CRD or use defaults + version := ">= 0.0.0-0" + reconcileStrategy := "Revision" + // TODO: Add Version and ReconcileStrategy fields to CozystackResourceDefinitionChart if needed + + // Build expected SourceRef + expectedSourceRef := helmv2.CrossNamespaceObjectReference{ + Kind: crd.Spec.Release.Chart.SourceRef.Kind, + Name: crd.Spec.Release.Chart.SourceRef.Name, + Namespace: crd.Spec.Release.Chart.SourceRef.Namespace, + } + + if hrCopy.Spec.Chart == nil { + // Need to create Chart spec + hrCopy.Spec.Chart = &helmv2.HelmChartTemplate{ + Spec: helmv2.HelmChartTemplateSpec{ + Chart: crd.Spec.Release.Chart.Name, + Version: version, + ReconcileStrategy: reconcileStrategy, + SourceRef: expectedSourceRef, + }, + } + updated = true + } else { + // Update existing Chart spec + if hrCopy.Spec.Chart.Spec.Chart != crd.Spec.Release.Chart.Name || + hrCopy.Spec.Chart.Spec.SourceRef != expectedSourceRef { + hrCopy.Spec.Chart.Spec.Chart = crd.Spec.Release.Chart.Name + hrCopy.Spec.Chart.Spec.SourceRef = expectedSourceRef + updated = true + } + } + + if updated { + logger.V(4).Info("Updating HelmRelease chart", "name", hr.Name, "namespace", hr.Namespace) + if err := r.Update(ctx, hrCopy); err != nil { + return fmt.Errorf("failed to update HelmRelease: %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/packages/core/flux-aio/templates/fluxcd.yaml b/internal/fluxinstall/manifests/fluxcd.yaml similarity index 99% rename from packages/core/flux-aio/templates/fluxcd.yaml rename to internal/fluxinstall/manifests/fluxcd.yaml index 71a354c6..237db089 100644 --- a/packages/core/flux-aio/templates/fluxcd.yaml +++ b/internal/fluxinstall/manifests/fluxcd.yaml @@ -11623,7 +11623,6 @@ spec: value: /tmp/.sigstore - name: NO_PROXY value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} image: ghcr.io/fluxcd/source-controller:v1.7.3 imagePullPolicy: IfNotPresent livenessProbe: @@ -11694,7 +11693,6 @@ spec: value: /tmp/.sigstore - name: NO_PROXY value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} image: ghcr.io/fluxcd/kustomize-controller:v1.7.2 imagePullPolicy: IfNotPresent livenessProbe: @@ -11760,7 +11758,6 @@ spec: value: /tmp/.sigstore - name: NO_PROXY value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} image: ghcr.io/fluxcd/helm-controller:v1.4.3 imagePullPolicy: IfNotPresent livenessProbe: @@ -11823,7 +11820,6 @@ spec: value: /tmp/.sigstore - name: NO_PROXY value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} image: ghcr.io/fluxcd/notification-controller:v1.7.4 imagePullPolicy: IfNotPresent livenessProbe: @@ -11894,7 +11890,6 @@ spec: value: /tmp/.sigstore - name: NO_PROXY value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} image: ghcr.io/fluxcd/source-watcher:v2.0.2 imagePullPolicy: IfNotPresent livenessProbe: @@ -11936,7 +11931,6 @@ spec: name: data - mountPath: /tmp name: tmp - dnsPolicy: ClusterFirstWithHostNet hostNetwork: true priorityClassName: system-cluster-critical securityContext: diff --git a/internal/lineagecontrollerwebhook/config.go b/internal/lineagecontrollerwebhook/config.go index 4ca45c13..7204ab66 100644 --- a/internal/lineagecontrollerwebhook/config.go +++ b/internal/lineagecontrollerwebhook/config.go @@ -2,49 +2,72 @@ 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 + 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), + appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition), }) } }) } -func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) { - cfg, ok := l.config.Load().(*runtimeConfig) +// getApplicationLabel safely extracts an application label from HelmRelease +func getApplicationLabel(hr *helmv2.HelmRelease, key string) (string, error) { + if hr.Labels == nil { + return "", fmt.Errorf("cannot map helm release %s/%s to dynamic app: labels are nil", hr.Namespace, hr.Name) + } + val, ok := hr.Labels[key] 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 %s label", hr.Namespace, hr.Name, key) } - 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}] - if !ok { - return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name) - } - return "apps.cozystack.io/v1alpha1", val.Spec.Application.Kind, val.Spec.Release.Prefix, nil + return val, nil +} + +func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) { + // Extract application metadata from labels + appKind, err := getApplicationLabel(hr, "apps.cozystack.io/application.kind") + if err != nil { + return "", "", "", err + } + + appGroup, err := getApplicationLabel(hr, "apps.cozystack.io/application.group") + if err != nil { + return "", "", "", err + } + + appName, err := getApplicationLabel(hr, "apps.cozystack.io/application.name") + if err != nil { + return "", "", "", err + } + + // 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) + + // Validate the derived prefix + // This ensures correctness when appName appears multiple times in hr.Name + if prefix+appName != hr.Name { + return "", "", "", fmt.Errorf("cannot derive prefix from helm release %s/%s: name does not end with application name %s", hr.Namespace, hr.Name, appName) + } + + return apiVersion, appKind, prefix, nil } diff --git a/internal/lineagecontrollerwebhook/controller.go b/internal/lineagecontrollerwebhook/controller.go index e7522f62..092d16e7 100644 --- a/internal/lineagecontrollerwebhook/controller.go +++ b/internal/lineagecontrollerwebhook/controller.go @@ -24,25 +24,15 @@ func (c *LineageControllerWebhook) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } cfg := &runtimeConfig{ - chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition), - appCRDMap: make(map[appRef]*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 { diff --git a/internal/operator/bundle_reconciler.go.old b/internal/operator/bundle_reconciler.go.old new file mode 100644 index 00000000..ddaa1303 --- /dev/null +++ b/internal/operator/bundle_reconciler.go.old @@ -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/package_reconciler.go b/internal/operator/package_reconciler.go new file mode 100644 index 00000000..8ac5ed74 --- /dev/null +++ b/internal/operator/package_reconciler.go @@ -0,0 +1,889 @@ +/* +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" + "fmt" + "strings" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + helmv2 "github.com/fluxcd/helm-controller/api/v2" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + 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" +) + +// PackageReconciler reconciles Package resources +type PackageReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=cozystack.io,resources=packages,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=cozystack.io,resources=packages/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=cozystack.io,resources=packagesources,verbs=get;list;watch +// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,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 *PackageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + pkg := &cozyv1alpha1.Package{} + if err := r.Get(ctx, req.NamespacedName, pkg); err != nil { + if apierrors.IsNotFound(err) { + // Resource not found, return (ownerReference will handle cleanup) + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // Get PackageSource with the same name + packageSource := &cozyv1alpha1.PackageSource{} + if err := r.Get(ctx, types.NamespacedName{Name: pkg.Name}, packageSource); err != nil { + if apierrors.IsNotFound(err) { + meta.SetStatusCondition(&pkg.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionFalse, + Reason: "PackageSourceNotFound", + Message: fmt.Sprintf("PackageSource %s not found", pkg.Name), + }) + if err := r.Status().Update(ctx, pkg); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // Determine variant (default to "default" if not specified) + variantName := pkg.Spec.Variant + if variantName == "" { + variantName = "default" + } + + // Find the variant in PackageSource + var variant *cozyv1alpha1.Variant + for i := range packageSource.Spec.Variants { + if packageSource.Spec.Variants[i].Name == variantName { + variant = &packageSource.Spec.Variants[i] + break + } + } + + if variant == nil { + meta.SetStatusCondition(&pkg.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionFalse, + Reason: "VariantNotFound", + Message: fmt.Sprintf("Variant %s not found in PackageSource %s", variantName, pkg.Name), + }) + if err := r.Status().Update(ctx, pkg); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + // Reconcile namespaces from components + if err := r.reconcileNamespaces(ctx, pkg, variant); err != nil { + logger.Error(err, "failed to reconcile namespaces") + return ctrl.Result{}, err + } + + // Validate variant dependencies before creating HelmReleases + // If dependencies are missing, we don't create new HelmReleases but don't delete existing ones + if err := r.validateVariantDependencies(ctx, pkg, variant); err != nil { + logger.Info("variant dependencies not ready, skipping HelmRelease creation", "package", pkg.Name, "error", err) + meta.SetStatusCondition(&pkg.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionFalse, + Reason: "DependenciesNotReady", + Message: fmt.Sprintf("Variant dependencies not ready: %v", err), + }) + if err := r.Status().Update(ctx, pkg); err != nil { + return ctrl.Result{}, err + } + // Return success to avoid requeue, but don't create HelmReleases + return ctrl.Result{}, nil + } + + // Create HelmReleases for components with Install section + helmReleaseCount := 0 + for _, component := range variant.Components { + // Skip components without Install section + if component.Install == nil { + continue + } + + // Check if component is disabled via Package spec + if pkgComponent, ok := pkg.Spec.Components[component.Name]; ok { + if pkgComponent.Enabled != nil && !*pkgComponent.Enabled { + logger.V(1).Info("skipping disabled component", "package", pkg.Name, "component", component.Name) + continue + } + } + + // Build artifact name: -- (with dots replaced by dashes) + artifactName := fmt.Sprintf("%s-%s-%s", + strings.ReplaceAll(packageSource.Name, ".", "-"), + strings.ReplaceAll(variantName, ".", "-"), + strings.ReplaceAll(component.Name, ".", "-")) + + // Determine namespace (from Install or default to cozy-system) + namespace := component.Install.Namespace + if namespace == "" { + namespace = "cozy-system" + } + + // Determine release name (from Install or use component name) + releaseName := component.Install.ReleaseName + if releaseName == "" { + releaseName = component.Name + } + + // Build labels + labels := make(map[string]string) + labels["cozystack.io/package"] = pkg.Name + if component.Install.Privileged { + labels["cozystack.io/privileged"] = "true" + } + + // Create HelmRelease + hr := &helmv2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: releaseName, + Namespace: namespace, + Labels: labels, + }, + Spec: helmv2.HelmReleaseSpec{ + Interval: metav1.Duration{Duration: 5 * 60 * 1000000000}, // 5m + ChartRef: &helmv2.CrossNamespaceSourceReference{ + Kind: "ExternalArtifact", + Name: artifactName, + Namespace: "cozy-system", + }, + Install: &helmv2.Install{ + Remediation: &helmv2.InstallRemediation{ + Retries: -1, + }, + }, + Upgrade: &helmv2.Upgrade{ + Remediation: &helmv2.UpgradeRemediation{ + Retries: -1, + }, + }, + }, + } + + // Set ownerReference + hr.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: pkg.APIVersion, + Kind: pkg.Kind, + Name: pkg.Name, + UID: pkg.UID, + Controller: func() *bool { b := true; return &b }(), + }, + } + + // Merge values from Package spec if provided + if pkgComponent, ok := pkg.Spec.Components[component.Name]; ok && pkgComponent.Values != nil { + hr.Spec.Values = pkgComponent.Values + } + + // Build DependsOn from component Install and variant DependsOn + dependsOn, err := r.buildDependsOn(ctx, pkg, packageSource, variant, &component) + if err != nil { + logger.Error(err, "failed to build DependsOn", "component", component.Name) + meta.SetStatusCondition(&pkg.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionFalse, + Reason: "DependsOnFailed", + Message: fmt.Sprintf("Failed to build DependsOn for component %s: %v", component.Name, err), + }) + if err := r.Status().Update(ctx, pkg); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + if len(dependsOn) > 0 { + hr.Spec.DependsOn = dependsOn + } + + if err := r.createOrUpdateHelmRelease(ctx, hr); err != nil { + logger.Error(err, "failed to reconcile HelmRelease", "name", releaseName, "namespace", namespace) + meta.SetStatusCondition(&pkg.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionFalse, + Reason: "HelmReleaseFailed", + Message: fmt.Sprintf("Failed to create HelmRelease %s: %v", releaseName, err), + }) + if err := r.Status().Update(ctx, pkg); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, err + } + + helmReleaseCount++ + logger.Info("reconciled HelmRelease", "package", pkg.Name, "component", component.Name, "releaseName", releaseName, "namespace", namespace) + } + + // Cleanup orphaned HelmReleases + if err := r.cleanupOrphanedHelmReleases(ctx, pkg, variant); err != nil { + logger.Error(err, "failed to cleanup orphaned HelmReleases") + // Don't return error, continue with status update + } + + // Update status with success message + message := fmt.Sprintf("reconciliation succeeded, generated %d helmrelease(s)", helmReleaseCount) + meta.SetStatusCondition(&pkg.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionTrue, + Reason: "ReconciliationSucceeded", + Message: message, + }) + + if err := r.Status().Update(ctx, pkg); err != nil { + return ctrl.Result{}, err + } + + logger.Info("reconciled Package", "name", pkg.Name, "helmReleaseCount", helmReleaseCount) + + // Trigger reconcile for Packages that depend on this Package + if err := r.triggerDependentPackages(ctx, pkg.Name); err != nil { + logger.Error(err, "failed to trigger dependent Packages", "package", pkg.Name) + // Don't return error, this is best-effort + } + + return ctrl.Result{}, nil +} + +// createOrUpdateHelmRelease creates or updates a HelmRelease +func (r *PackageReconciler) createOrUpdateHelmRelease(ctx context.Context, hr *helmv2.HelmRelease) error { + existing := &helmv2.HelmRelease{} + key := types.NamespacedName{ + Name: hr.Name, + Namespace: hr.Namespace, + } + + err := r.Get(ctx, key, existing) + if apierrors.IsNotFound(err) { + return r.Create(ctx, hr) + } else if err != nil { + return err + } + + // Preserve resource version + hr.SetResourceVersion(existing.GetResourceVersion()) + + // Merge labels + labels := hr.GetLabels() + if labels == nil { + labels = make(map[string]string) + } + for k, v := range existing.GetLabels() { + if _, ok := labels[k]; !ok { + labels[k] = v + } + } + hr.SetLabels(labels) + + // Merge annotations + annotations := hr.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + for k, v := range existing.GetAnnotations() { + if _, ok := annotations[k]; !ok { + annotations[k] = v + } + } + hr.SetAnnotations(annotations) + + // Update Spec + existing.Spec = hr.Spec + existing.SetLabels(hr.GetLabels()) + existing.SetAnnotations(hr.GetAnnotations()) + existing.SetOwnerReferences(hr.GetOwnerReferences()) + + return r.Update(ctx, existing) +} + +// buildDependsOn builds DependsOn list for a component +// Includes: +// 1. Dependencies from component.Install.DependsOn (with namespace from referenced component) +// 2. Dependencies from variant.DependsOn (all components with Install from referenced Package) +func (r *PackageReconciler) buildDependsOn(ctx context.Context, pkg *cozyv1alpha1.Package, packageSource *cozyv1alpha1.PackageSource, variant *cozyv1alpha1.Variant, component *cozyv1alpha1.Component) ([]helmv2.DependencyReference, error) { + logger := log.FromContext(ctx) + dependsOn := []helmv2.DependencyReference{} + + // Build map of component names to their release names and namespaces in current variant + componentMap := make(map[string]struct { + releaseName string + namespace string + }) + for _, comp := range variant.Components { + if comp.Install == nil { + continue + } + compNamespace := comp.Install.Namespace + if compNamespace == "" { + compNamespace = "cozy-system" + } + compReleaseName := comp.Install.ReleaseName + if compReleaseName == "" { + compReleaseName = comp.Name + } + componentMap[comp.Name] = struct { + releaseName string + namespace string + }{ + releaseName: compReleaseName, + namespace: compNamespace, + } + } + + // Add dependencies from component.Install.DependsOn + if len(component.Install.DependsOn) > 0 { + for _, depName := range component.Install.DependsOn { + depComp, ok := componentMap[depName] + if !ok { + return nil, fmt.Errorf("component %s not found in variant for dependency %s", depName, component.Name) + } + dependsOn = append(dependsOn, helmv2.DependencyReference{ + Name: depComp.releaseName, + Namespace: depComp.namespace, + }) + logger.V(1).Info("added component dependency", "component", component.Name, "dependsOn", depName, "releaseName", depComp.releaseName, "namespace", depComp.namespace) + } + } + + // Add dependencies from variant.DependsOn + if len(variant.DependsOn) > 0 { + for _, depPackageName := range variant.DependsOn { + // Check if dependency is in IgnoreDependencies + ignore := false + for _, ignoreDep := range pkg.Spec.IgnoreDependencies { + if ignoreDep == depPackageName { + ignore = true + break + } + } + if ignore { + logger.V(1).Info("ignoring dependency", "package", pkg.Name, "dependency", depPackageName) + continue + } + + // Get the Package + depPackage := &cozyv1alpha1.Package{} + if err := r.Get(ctx, types.NamespacedName{Name: depPackageName}, depPackage); err != nil { + if apierrors.IsNotFound(err) { + return nil, fmt.Errorf("dependent Package %s not found", depPackageName) + } + return nil, fmt.Errorf("failed to get dependent Package %s: %w", depPackageName, err) + } + + // Get the variant from dependent Package + depVariantName := depPackage.Spec.Variant + if depVariantName == "" { + depVariantName = "default" + } + + // Get the PackageSource + depPackageSource := &cozyv1alpha1.PackageSource{} + if err := r.Get(ctx, types.NamespacedName{Name: depPackageName}, depPackageSource); err != nil { + if apierrors.IsNotFound(err) { + return nil, fmt.Errorf("dependent PackageSource %s not found", depPackageName) + } + return nil, fmt.Errorf("failed to get dependent PackageSource %s: %w", depPackageName, err) + } + + // Find the variant in PackageSource + var depVariant *cozyv1alpha1.Variant + for i := range depPackageSource.Spec.Variants { + if depPackageSource.Spec.Variants[i].Name == depVariantName { + depVariant = &depPackageSource.Spec.Variants[i] + break + } + } + + if depVariant == nil { + return nil, fmt.Errorf("dependent variant %s not found in PackageSource %s", depVariantName, depPackageName) + } + + // Add all components with Install from dependent variant + for _, depComp := range depVariant.Components { + if depComp.Install == nil { + continue + } + + // Check if component is disabled in dependent Package + if depPkgComponent, ok := depPackage.Spec.Components[depComp.Name]; ok { + if depPkgComponent.Enabled != nil && !*depPkgComponent.Enabled { + continue + } + } + + depCompNamespace := depComp.Install.Namespace + if depCompNamespace == "" { + depCompNamespace = "cozy-system" + } + depCompReleaseName := depComp.Install.ReleaseName + if depCompReleaseName == "" { + depCompReleaseName = depComp.Name + } + + dependsOn = append(dependsOn, helmv2.DependencyReference{ + Name: depCompReleaseName, + Namespace: depCompNamespace, + }) + logger.V(1).Info("added variant dependency", "package", pkg.Name, "dependency", depPackageName, "component", depComp.Name, "releaseName", depCompReleaseName, "namespace", depCompNamespace) + } + } + } + + return dependsOn, nil +} + +// validateVariantDependencies validates that all variant dependencies exist +// Returns error if any dependency is missing +func (r *PackageReconciler) validateVariantDependencies(ctx context.Context, pkg *cozyv1alpha1.Package, variant *cozyv1alpha1.Variant) error { + logger := log.FromContext(ctx) + + if len(variant.DependsOn) == 0 { + return nil + } + + for _, depPackageName := range variant.DependsOn { + // Check if dependency is in IgnoreDependencies + ignore := false + for _, ignoreDep := range pkg.Spec.IgnoreDependencies { + if ignoreDep == depPackageName { + ignore = true + break + } + } + if ignore { + logger.V(1).Info("ignoring dependency", "package", pkg.Name, "dependency", depPackageName) + continue + } + + // Get the Package + depPackage := &cozyv1alpha1.Package{} + if err := r.Get(ctx, types.NamespacedName{Name: depPackageName}, depPackage); err != nil { + if apierrors.IsNotFound(err) { + return fmt.Errorf("dependent Package %s not found", depPackageName) + } + return fmt.Errorf("failed to get dependent Package %s: %w", depPackageName, err) + } + + // Get the PackageSource + depPackageSource := &cozyv1alpha1.PackageSource{} + if err := r.Get(ctx, types.NamespacedName{Name: depPackageName}, depPackageSource); err != nil { + if apierrors.IsNotFound(err) { + return fmt.Errorf("dependent PackageSource %s not found", depPackageName) + } + return fmt.Errorf("failed to get dependent PackageSource %s: %w", depPackageName, err) + } + + // Get the variant from dependent Package + depVariantName := depPackage.Spec.Variant + if depVariantName == "" { + depVariantName = "default" + } + + // Find the variant in PackageSource + var depVariant *cozyv1alpha1.Variant + for i := range depPackageSource.Spec.Variants { + if depPackageSource.Spec.Variants[i].Name == depVariantName { + depVariant = &depPackageSource.Spec.Variants[i] + break + } + } + + if depVariant == nil { + return fmt.Errorf("dependent variant %s not found in PackageSource %s", depVariantName, depPackageName) + } + } + + return nil +} + +// reconcileNamespaces creates or updates namespaces based on components in the variant +func (r *PackageReconciler) reconcileNamespaces(ctx context.Context, pkg *cozyv1alpha1.Package, variant *cozyv1alpha1.Variant) error { + logger := log.FromContext(ctx) + + // Collect namespaces from components + // Map: namespace -> {isPrivileged} + type namespaceInfo struct { + privileged bool + } + namespacesMap := make(map[string]namespaceInfo) + + for _, component := range variant.Components { + // Skip components without Install section + if component.Install == nil { + continue + } + + // Check if component is disabled via Package spec + if pkgComponent, ok := pkg.Spec.Components[component.Name]; ok { + if pkgComponent.Enabled != nil && !*pkgComponent.Enabled { + continue + } + } + + // Namespace must be set + namespace := component.Install.Namespace + if namespace == "" { + return fmt.Errorf("component %s has empty namespace in Install section", component.Name) + } + + info, exists := namespacesMap[namespace] + if !exists { + info = namespaceInfo{ + privileged: false, + } + } + + // If component is privileged, mark namespace as privileged + if component.Install.Privileged { + info.privileged = true + } + + namespacesMap[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" + } + + 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) + } + + return nil +} + +// createOrUpdateNamespace creates or updates a namespace +func (r *PackageReconciler) 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) +} + +// cleanupOrphanedHelmReleases removes HelmReleases that are no longer needed +func (r *PackageReconciler) cleanupOrphanedHelmReleases(ctx context.Context, pkg *cozyv1alpha1.Package, variant *cozyv1alpha1.Variant) error { + logger := log.FromContext(ctx) + + // Build map of desired HelmRelease names (from components with Install) + desiredReleases := make(map[types.NamespacedName]bool) + for _, component := range variant.Components { + if component.Install == nil { + continue + } + + // Check if component is disabled via Package spec + if pkgComponent, ok := pkg.Spec.Components[component.Name]; ok { + if pkgComponent.Enabled != nil && !*pkgComponent.Enabled { + continue + } + } + + namespace := component.Install.Namespace + if namespace == "" { + namespace = "cozy-system" + } + + releaseName := component.Install.ReleaseName + if releaseName == "" { + releaseName = component.Name + } + + desiredReleases[types.NamespacedName{ + Name: releaseName, + Namespace: namespace, + }] = true + } + + // Find all HelmReleases owned by this Package + hrList := &helmv2.HelmReleaseList{} + if err := r.List(ctx, hrList, client.MatchingLabels{ + "cozystack.io/package": pkg.Name, + }); err != nil { + return err + } + + // Delete HelmReleases that are not in desired list + for _, hr := range hrList.Items { + key := types.NamespacedName{ + Name: hr.Name, + Namespace: hr.Namespace, + } + if !desiredReleases[key] { + logger.Info("deleting orphaned HelmRelease", "name", hr.Name, "namespace", hr.Namespace, "package", pkg.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) + } + } + } + + return nil +} + +// triggerDependentPackages triggers reconcile for all Packages that depend on the given Package +func (r *PackageReconciler) triggerDependentPackages(ctx context.Context, packageName string) error { + logger := log.FromContext(ctx) + + // Get all Packages + packageList := &cozyv1alpha1.PackageList{} + if err := r.List(ctx, packageList); err != nil { + return fmt.Errorf("failed to list Packages: %w", err) + } + + // For each Package, check if it depends on the given Package + for _, pkg := range packageList.Items { + // Skip the Package itself + if pkg.Name == packageName { + continue + } + + // Get PackageSource + packageSource := &cozyv1alpha1.PackageSource{} + if err := r.Get(ctx, types.NamespacedName{Name: pkg.Name}, packageSource); err != nil { + if apierrors.IsNotFound(err) { + continue + } + logger.V(1).Error(err, "failed to get PackageSource", "package", pkg.Name) + continue + } + + // Determine variant + variantName := pkg.Spec.Variant + if variantName == "" { + variantName = "default" + } + + // Find variant + var variant *cozyv1alpha1.Variant + for i := range packageSource.Spec.Variants { + if packageSource.Spec.Variants[i].Name == variantName { + variant = &packageSource.Spec.Variants[i] + break + } + } + + if variant == nil { + continue + } + + // Check if this Package depends on the given Package + dependsOn := false + for _, dep := range variant.DependsOn { + // Check if dependency is in IgnoreDependencies + ignore := false + for _, ignoreDep := range pkg.Spec.IgnoreDependencies { + if ignoreDep == dep { + ignore = true + break + } + } + if ignore { + continue + } + + if dep == packageName { + dependsOn = true + break + } + } + + if dependsOn { + logger.V(1).Info("triggering reconcile for dependent Package", "package", pkg.Name, "dependency", packageName) + // Trigger reconcile by updating the Package (add annotation or just requeue) + // We can't directly requeue from here, but we can update the Package to trigger reconcile + // Actually, we can use the client to trigger an update, but that might cause infinite loop + // Better approach: use event handler or just log and let the watch handle it + // For now, we'll just log - the watch on PackageSource should handle it + // But we need a way to trigger reconcile... + // Let's add an annotation to trigger reconcile + if pkg.Annotations == nil { + pkg.Annotations = make(map[string]string) + } + pkg.Annotations["cozystack.io/trigger-reconcile"] = fmt.Sprintf("%d", metav1.Now().Unix()) + if err := r.Update(ctx, &pkg); err != nil { + logger.V(1).Error(err, "failed to trigger reconcile for dependent Package", "package", pkg.Name) + } + } + } + + return nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PackageReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + Named("cozystack-package"). + For(&cozyv1alpha1.Package{}). + Watches( + &cozyv1alpha1.PackageSource{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + ps, ok := obj.(*cozyv1alpha1.PackageSource) + if !ok { + return nil + } + // Find Package with the same name as PackageSource + // PackageSource and Package share the same name + pkg := &cozyv1alpha1.Package{} + if err := mgr.GetClient().Get(ctx, types.NamespacedName{Name: ps.Name}, pkg); err != nil { + // Package not found, that's ok - it might not exist yet + return nil + } + // Trigger reconcile for the corresponding Package + return []reconcile.Request{{ + NamespacedName: types.NamespacedName{ + Name: pkg.Name, + }, + }} + }), + ). + Watches( + &cozyv1alpha1.Package{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + updatedPkg, ok := obj.(*cozyv1alpha1.Package) + if !ok { + return nil + } + // Find all Packages that depend on this Package + packageList := &cozyv1alpha1.PackageList{} + if err := mgr.GetClient().List(ctx, packageList); err != nil { + return nil + } + var requests []reconcile.Request + for _, pkg := range packageList.Items { + if pkg.Name == updatedPkg.Name { + continue // Skip the Package itself + } + // Get PackageSource to check dependencies + packageSource := &cozyv1alpha1.PackageSource{} + if err := mgr.GetClient().Get(ctx, types.NamespacedName{Name: pkg.Name}, packageSource); err != nil { + continue + } + // Determine variant + variantName := pkg.Spec.Variant + if variantName == "" { + variantName = "default" + } + // Find variant + for _, variant := range packageSource.Spec.Variants { + if variant.Name == variantName { + // Check if this variant depends on updatedPkg + for _, dep := range variant.DependsOn { + // Check if dependency is in IgnoreDependencies + ignore := false + for _, ignoreDep := range pkg.Spec.IgnoreDependencies { + if ignoreDep == dep { + ignore = true + break + } + } + if ignore { + continue + } + if dep == updatedPkg.Name { + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: pkg.Name, + }, + }) + break + } + } + break + } + } + } + return requests + }), + ). + Complete(r) +} diff --git a/internal/operator/packagesource_reconciler.go b/internal/operator/packagesource_reconciler.go new file mode 100644 index 00000000..d0b5db85 --- /dev/null +++ b/internal/operator/packagesource_reconciler.go @@ -0,0 +1,468 @@ +/* +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" + "fmt" + "strings" + + cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1" + sourcewatcherv1beta1 "github.com/fluxcd/source-watcher/api/v2/v1beta1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + 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" +) + +// PackageSourceReconciler reconciles PackageSource resources +type PackageSourceReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=cozystack.io,resources=packagesources,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=cozystack.io,resources=packagesources/status,verbs=get;update;patch +// +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 *PackageSourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + + packageSource := &cozyv1alpha1.PackageSource{} + if err := r.Get(ctx, req.NamespacedName, packageSource); err != nil { + if apierrors.IsNotFound(err) { + // Resource not found, return (ownerReference will handle cleanup) + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + + // Generate ArtifactGenerator for package source + if err := r.reconcileArtifactGenerators(ctx, packageSource); err != nil { + logger.Error(err, "failed to reconcile ArtifactGenerator") + return ctrl.Result{}, err + } + + // Update PackageSource status (variants and conditions from ArtifactGenerator) + if err := r.updateStatus(ctx, packageSource); err != nil { + logger.Error(err, "failed to update status") + // Don't return error, status update is not critical + } + + return ctrl.Result{}, nil +} + +// reconcileArtifactGenerators generates a single ArtifactGenerator for the package source +// Creates one ArtifactGenerator per package source with all OutputArtifacts from components +func (r *PackageSourceReconciler) reconcileArtifactGenerators(ctx context.Context, packageSource *cozyv1alpha1.PackageSource) error { + logger := log.FromContext(ctx) + + // Check if SourceRef is set + if packageSource.Spec.SourceRef == nil { + logger.Info("skipping ArtifactGenerator creation, SourceRef not set", "packageSource", packageSource.Name) + return nil + } + + // Build library map from all variants + // Map key is the library name (from lib.Name or extracted from path) + // This allows components to reference libraries by name + libraryMap := make(map[string]cozyv1alpha1.Library) + for _, variant := range packageSource.Spec.Variants { + for _, lib := range variant.Libraries { + libName := lib.Name + if libName == "" { + // If library name is not set, extract from path + libName = r.getPackageNameFromPath(lib.Path) + } + if libName != "" { + // Store library with the resolved name + libraryMap[libName] = lib + } + } + } + + // Namespace is always cozy-system + namespace := "cozy-system" + // ArtifactGenerator name is the package source name + agName := packageSource.Name + + // Collect all OutputArtifacts + outputArtifacts := []sourcewatcherv1beta1.OutputArtifact{} + + // Process all variants and their components + for _, variant := range packageSource.Spec.Variants { + for _, component := range variant.Components { + // Skip components without path + if component.Path == "" { + logger.V(1).Info("skipping component without path", "packageSource", packageSource.Name, "variant", variant.Name, "component", component.Name) + continue + } + + logger.V(1).Info("processing component", "packageSource", packageSource.Name, "variant", variant.Name, "component", component.Name, "path", component.Path) + + // Extract component name from path (last component) + componentPathName := r.getPackageNameFromPath(component.Path) + if componentPathName == "" { + logger.Info("skipping component with invalid path", "packageSource", packageSource.Name, "variant", variant.Name, "component", component.Name, "path", component.Path) + continue + } + + // Get basePath with default values + basePath := r.getBasePath(packageSource) + + // Build copy operations + copyOps := []sourcewatcherv1beta1.CopyOperation{ + { + From: r.buildSourcePath(packageSource.Spec.SourceRef.Name, basePath, component.Path), + To: fmt.Sprintf("@artifact/%s/", componentPathName), + }, + } + + // Add libraries if specified + for _, libName := range component.Libraries { + if lib, ok := libraryMap[libName]; ok { + copyOps = append(copyOps, sourcewatcherv1beta1.CopyOperation{ + From: r.buildSourcePath(packageSource.Spec.SourceRef.Name, basePath, lib.Path), + To: fmt.Sprintf("@artifact/%s/charts/%s/", componentPathName, libName), + }) + } + } + + // Add valuesFiles if specified + for i, valuesFile := range component.ValuesFiles { + strategy := "Merge" + if i == 0 { + strategy = "Overwrite" + } + copyOps = append(copyOps, sourcewatcherv1beta1.CopyOperation{ + From: r.buildSourceFilePath(packageSource.Spec.SourceRef.Name, basePath, fmt.Sprintf("%s/%s", component.Path, valuesFile)), + To: fmt.Sprintf("@artifact/%s/values.yaml", componentPathName), + Strategy: strategy, + }) + } + + // Artifact name: -- + // Replace dots with dashes to comply with Kubernetes naming requirements + artifactName := fmt.Sprintf("%s-%s-%s", + strings.ReplaceAll(packageSource.Name, ".", "-"), + strings.ReplaceAll(variant.Name, ".", "-"), + strings.ReplaceAll(component.Name, ".", "-")) + + outputArtifacts = append(outputArtifacts, sourcewatcherv1beta1.OutputArtifact{ + Name: artifactName, + Copy: copyOps, + }) + + logger.Info("added OutputArtifact for component", "packageSource", packageSource.Name, "variant", variant.Name, "component", component.Name, "artifactName", artifactName) + } + } + + // If there are no OutputArtifacts, return (ownerReference will handle cleanup if needed) + if len(outputArtifacts) == 0 { + logger.Info("no OutputArtifacts to generate, skipping ArtifactGenerator creation", "packageSource", packageSource.Name) + return nil + } + + // Build labels + labels := make(map[string]string) + labels["cozystack.io/packagesource"] = packageSource.Name + + // Create single ArtifactGenerator for the package source + ag := &sourcewatcherv1beta1.ArtifactGenerator{ + ObjectMeta: metav1.ObjectMeta{ + Name: agName, + Namespace: namespace, + Labels: labels, + }, + Spec: sourcewatcherv1beta1.ArtifactGeneratorSpec{ + Sources: []sourcewatcherv1beta1.SourceReference{ + { + Alias: packageSource.Spec.SourceRef.Name, + Kind: packageSource.Spec.SourceRef.Kind, + Name: packageSource.Spec.SourceRef.Name, + Namespace: packageSource.Spec.SourceRef.Namespace, + }, + }, + OutputArtifacts: outputArtifacts, + }, + } + + // Set ownerReference + ag.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: packageSource.APIVersion, + Kind: packageSource.Kind, + Name: packageSource.Name, + UID: packageSource.UID, + Controller: func() *bool { b := true; return &b }(), + }, + } + + logger.Info("creating ArtifactGenerator for package source", "packageSource", packageSource.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 package source", "name", agName, "namespace", namespace, "outputArtifactCount", len(outputArtifacts)) + + return nil +} + +// Helper functions +func (r *PackageSourceReconciler) 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 *PackageSourceReconciler) getBasePath(packageSource *cozyv1alpha1.PackageSource) string { + // If path is explicitly set in SourceRef, use it (but normalize "/" to empty) + if packageSource.Spec.SourceRef.Path != "" { + path := strings.Trim(packageSource.Spec.SourceRef.Path, "/") + // If path is "/" or empty after trim, return empty string + if path == "" { + return "" + } + return path + } + // Default values based on kind + if packageSource.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 *PackageSourceReconciler) buildSourcePath(sourceName, basePath, path string) string { + // Remove leading/trailing slashes and combine + parts := []string{} + if basePath != "" { + trimmed := strings.Trim(basePath, "/") + if trimmed != "" { + parts = append(parts, trimmed) + } + } + if path != "" { + trimmed := strings.Trim(path, "/") + if trimmed != "" { + parts = append(parts, trimmed) + } + } + + 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 *PackageSourceReconciler) buildSourceFilePath(sourceName, basePath, path string) string { + // Remove leading/trailing slashes and combine + parts := []string{} + if basePath != "" { + trimmed := strings.Trim(basePath, "/") + if trimmed != "" { + parts = append(parts, trimmed) + } + } + if path != "" { + trimmed := strings.Trim(path, "/") + if trimmed != "" { + parts = append(parts, trimmed) + } + } + + fullPath := strings.Join(parts, "/") + if fullPath == "" { + return fmt.Sprintf("@%s", sourceName) + } + return fmt.Sprintf("@%s/%s", sourceName, fullPath) +} + +// createOrUpdate creates or updates a resource +func (r *PackageSourceReconciler) 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 (OutputArtifacts and Sources) + 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 + } + } + + return r.Update(ctx, obj) +} + +// updateStatus updates PackageSource status (variants and conditions from ArtifactGenerator) +func (r *PackageSourceReconciler) updateStatus(ctx context.Context, packageSource *cozyv1alpha1.PackageSource) error { + logger := log.FromContext(ctx) + + // Update variants in status from spec + variantNames := make([]string, 0, len(packageSource.Spec.Variants)) + for _, variant := range packageSource.Spec.Variants { + variantNames = append(variantNames, variant.Name) + } + packageSource.Status.Variants = strings.Join(variantNames, ",") + + // Check if SourceRef is set + if packageSource.Spec.SourceRef == nil { + // Set status to unknown if SourceRef is not set + meta.SetStatusCondition(&packageSource.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionUnknown, + Reason: "SourceRefNotSet", + Message: "SourceRef is not configured", + }) + return r.Status().Update(ctx, packageSource) + } + + // Get ArtifactGenerator + ag := &sourcewatcherv1beta1.ArtifactGenerator{} + agKey := types.NamespacedName{ + Name: packageSource.Name, + Namespace: "cozy-system", + } + + if err := r.Get(ctx, agKey, ag); err != nil { + if apierrors.IsNotFound(err) { + // ArtifactGenerator not found, set status to unknown + meta.SetStatusCondition(&packageSource.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionUnknown, + Reason: "ArtifactGeneratorNotFound", + Message: "ArtifactGenerator not found", + }) + return r.Status().Update(ctx, packageSource) + } + return fmt.Errorf("failed to get ArtifactGenerator: %w", err) + } + + // Find Ready condition in ArtifactGenerator + readyCondition := meta.FindStatusCondition(ag.Status.Conditions, "Ready") + if readyCondition == nil { + // No Ready condition in ArtifactGenerator, set status to unknown + meta.SetStatusCondition(&packageSource.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: metav1.ConditionUnknown, + Reason: "ArtifactGeneratorNotReady", + Message: "ArtifactGenerator Ready condition not found", + }) + return r.Status().Update(ctx, packageSource) + } + + // Copy Ready condition from ArtifactGenerator to PackageSource + meta.SetStatusCondition(&packageSource.Status.Conditions, metav1.Condition{ + Type: "Ready", + Status: readyCondition.Status, + Reason: readyCondition.Reason, + Message: readyCondition.Message, + ObservedGeneration: packageSource.Generation, + LastTransitionTime: readyCondition.LastTransitionTime, + }) + + logger.V(1).Info("updated PackageSource status from ArtifactGenerator", + "packageSource", packageSource.Name, + "status", readyCondition.Status, + "reason", readyCondition.Reason) + + return r.Status().Update(ctx, packageSource) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *PackageSourceReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + Named("cozystack-packagesource"). + For(&cozyv1alpha1.PackageSource{}). + Watches( + &sourcewatcherv1beta1.ArtifactGenerator{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + ag, ok := obj.(*sourcewatcherv1beta1.ArtifactGenerator) + if !ok { + return nil + } + // Find the PackageSource that owns this ArtifactGenerator by ownerReference + for _, ownerRef := range ag.OwnerReferences { + if ownerRef.Kind == "PackageSource" { + return []reconcile.Request{{ + NamespacedName: types.NamespacedName{ + Name: ownerRef.Name, + }, + }} + } + } + return nil + }), + ). + Complete(r) +} + diff --git a/packages/apps/bucket/templates/helmrelease.yaml b/packages/apps/bucket/templates/helmrelease.yaml index 45959572..01e7dfb2 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-bucket-application-default-bucket-system + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml index 73954e12..0a14d0f3 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-kubernetes-application-kubevirt-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..1114cd75 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-kubernetes-application-kubevirt-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..c5482735 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-kubernetes-application-kubevirt-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..da14661a 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-kubernetes-application-kubevirt-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 3ecbf1eb..06d48980 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-kubernetes-application-kubevirt-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..793cb070 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-kubernetes-application-kubevirt-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-kubernetes-application-kubevirt-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..16683f02 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-kubernetes-application-kubevirt-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..0b7953bb 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-kubernetes-application-kubevirt-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..0f147798 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-kubernetes-application-kubevirt-kubernetes-ingress-nginx + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml b/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml index 394e6bb4..8f99c62c 100644 --- a/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml @@ -7,15 +7,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: metrics-server - chart: - spec: - chart: cozy-metrics-server - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-metrics-server + 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 cf93f233..565fc5a9 100644 --- a/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml @@ -10,15 +10,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-kubernetes-application-kubevirt-kubernetes-monitoring-agents + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml index 63972126..6f9ef87d 100644 --- a/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml @@ -7,15 +7,10 @@ metadata: cozystack.io/target-cluster-name: {{ .Release.Name }} spec: releaseName: prometheus-operator-crds - chart: - spec: - chart: cozy-prometheus-operator-crds - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-prometheus-operator-crds + 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..29abc286 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-kubernetes-application-kubevirt-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 71bdc9ee..89393b7f 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-kubernetes-application-kubevirt-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..bee264a2 100644 --- a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml @@ -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-kubernetes-application-kubevirt-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..da53ea8d 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-kubernetes-application-kubevirt-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 83fa32d1..74105e5b 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-kubernetes-application-kubevirt-kubernetes-volumesnapshot-crd + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/nats/templates/nats.yaml b/packages/apps/nats/templates/nats.yaml index b05c87b5..0d66c8e8 100644 --- a/packages/apps/nats/templates/nats.yaml +++ b/packages/apps/nats/templates/nats.yaml @@ -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-nats-application-default-nats-system + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/etcd.yaml b/packages/apps/tenant/templates/etcd.yaml index 9db6ff6c..2af6e9df 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-etcd-application-default-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..a2b9488f 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-info-application-default-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..b9c853c2 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-ingress-application-default-ingress + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/monitoring.yaml b/packages/apps/tenant/templates/monitoring.yaml index edcc66d9..440ea790 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-monitoring-application-default-monitoring + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/seaweedfs.yaml b/packages/apps/tenant/templates/seaweedfs.yaml index 936e294b..c7b565b2 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-seaweedfs-application-default-seaweedfs + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/core/flux-aio/Makefile b/packages/core/flux-aio/Makefile index d50f317d..5583dafa 100644 --- a/packages/core/flux-aio/Makefile +++ b/packages/core/flux-aio/Makefile @@ -13,10 +13,10 @@ diff: cozypkg show -n $(NAMESPACE) $(NAME) --plain | kubectl diff -f- update: - timoni bundle build -f flux-aio.cue > templates/fluxcd.yaml - yq eval '(select(.kind == "Namespace") | .metadata.labels."pod-security.kubernetes.io/enforce") = "privileged"' -i templates/fluxcd.yaml - sed -i templates/fluxcd.yaml \ + timoni bundle build -f flux-aio.cue > manifests/fluxcd.yaml + yq eval '(select(.kind == "Namespace") | .metadata.labels."pod-security.kubernetes.io/enforce") = "privileged"' -i manifests/fluxcd.yaml + sed -i manifests/fluxcd.yaml \ -e '/timoni/d' \ - -e 's|\.cluster\.local\.,||g' -e 's|\.cluster\.local\,||g' -e 's|\.cluster\.local\.||g' \ - -e '/value: .svc/a \ {{- include "cozy.kubernetes_envs" . | nindent 12 }}' \ - -e '/hostNetwork: true/i \ dnsPolicy: ClusterFirstWithHostNet' + -e 's|\.cluster\.local\.,||g' -e 's|\.cluster\.local\,||g' -e 's|\.cluster\.local\.||g' + # TODO: solve dns issue with hostNetwork for installing helmreleases in tenant k8s clusters + #-e '/hostNetwork: true/i \ dnsPolicy: ClusterFirstWithHostNet' 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/templates/_helpers.tpl b/packages/core/flux-aio/templates/_helpers.tpl deleted file mode 100644 index e22979ba..00000000 --- a/packages/core/flux-aio/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/core/installer/1.yaml b/packages/core/installer/1.yaml new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/packages/core/installer/1.yaml @@ -0,0 +1 @@ + diff --git a/packages/core/installer/Makefile b/packages/core/installer/Makefile index a51e7b61..6679c136 100644 --- a/packages/core/installer/Makefile +++ b/packages/core/installer/Makefile @@ -20,18 +20,7 @@ diff: 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 \ - --cache-to type=inline \ - --metadata-file images/installer.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: pre-checks image-matchbox image-talos image-operator image-packages image-talos: test -f ../../../_out/assets/installer-amd64.tar || make talos-installer @@ -58,3 +47,31 @@ talos-initramfs talos-kernel talos-installer talos-iso talos-nocloud talos-metal 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- + +update-version: + TAG="$(call settag,$(TAG))" \ + yq -i '.cozystackOperator.cozystackVersion = strenv(TAG)' values.yaml + +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/cozystack-operator.json \ + $(BUILDX_ARGS) + 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-packages: update-version + 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 + export REPO="oci://$(REGISTRY)/platform-packages"; \ + 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.platformSource = (strenv(REPO) + "@" + strenv(DIGEST))' values.yaml diff --git a/packages/core/installer/crds/cozystack.io_packages.yaml b/packages/core/installer/crds/cozystack.io_packages.yaml new file mode 100644 index 00000000..04d19b72 --- /dev/null +++ b/packages/core/installer/crds/cozystack.io_packages.yaml @@ -0,0 +1,156 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: packages.cozystack.io +spec: + group: cozystack.io + names: + kind: Package + listKind: PackageList + plural: packages + shortNames: + - pkg + - pkgs + singular: package + scope: Cluster + versions: + - additionalPrinterColumns: + - description: Selected variant + jsonPath: .spec.variant + name: Variant + type: string + - description: Ready status + jsonPath: .status.conditions[?(@.type=='Ready')].status + name: Ready + type: string + - description: Ready message + jsonPath: .status.conditions[?(@.type=='Ready')].message + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Package is the Schema for the packages 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: PackageSpec defines the desired state of Package + properties: + components: + additionalProperties: + description: PackageRelease defines overrides for a specific component + properties: + enabled: + description: |- + Enabled indicates whether this component should be installed + If false, the component will be disabled even if it's defined in the PackageSource + type: boolean + values: + description: |- + Values contains Helm chart values as a JSON object + These values will be merged with the default values from the PackageSource + x-kubernetes-preserve-unknown-fields: true + type: object + description: |- + Components is a map of release name to component overrides + Allows overriding values and enabling/disabling specific components from the PackageSource + type: object + ignoreDependencies: + description: |- + IgnoreDependencies is a list of package source dependencies to ignore + Dependencies listed here will not be installed even if they are specified in the PackageSource + items: + type: string + type: array + variant: + description: |- + Variant is the name of the variant to use from the PackageSource + If not specified, defaults to "default" + type: string + type: object + status: + description: PackageStatus defines the observed state of Package + properties: + conditions: + description: Conditions represents the latest available observations + of a Package's state + 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: {} diff --git a/packages/core/installer/crds/cozystack.io_packagesources.yaml b/packages/core/installer/crds/cozystack.io_packagesources.yaml new file mode 100644 index 00000000..92a6335f --- /dev/null +++ b/packages/core/installer/crds/cozystack.io_packagesources.yaml @@ -0,0 +1,251 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: packagesources.cozystack.io +spec: + group: cozystack.io + names: + kind: PackageSource + listKind: PackageSourceList + plural: packagesources + shortNames: + - pkgsrc + - pkgsrcs + singular: packagesource + scope: Cluster + versions: + - additionalPrinterColumns: + - description: Package variants (comma-separated) + jsonPath: .status.variants + name: Variants + type: string + - description: Ready status + jsonPath: .status.conditions[?(@.type=='Ready')].status + name: Ready + type: string + - description: Ready message + jsonPath: .status.conditions[?(@.type=='Ready')].message + name: Status + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: PackageSource is the Schema for the packagesources 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: PackageSourceSpec defines the desired state of PackageSource + properties: + sourceRef: + description: SourceRef is the source reference for the package source + 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 + path: + description: |- + Path 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 + required: + - kind + - name + - namespace + type: object + variants: + description: |- + Variants is a list of package source variants + Each variant defines components, applications, dependencies, and libraries for a specific configuration + items: + description: Variant defines a single variant configuration + properties: + components: + description: Components is a list of Helm releases to be installed + as part of this variant + items: + description: Component defines a single Helm release component + within a package source + properties: + install: + description: Install defines installation parameters for + this component + properties: + dependsOn: + description: DependsOn is a list of component names + that must be installed before this component + items: + type: string + type: array + namespace: + description: Namespace is the Kubernetes namespace + where the release will be installed + 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 + If not specified, defaults to the component Name field + type: string + type: object + libraries: + description: |- + Libraries is a list of library names that this component depends on + These libraries must be defined at the variant level + items: + type: string + type: array + name: + description: Name is the unique identifier for this component + within the package source + type: string + path: + description: Path is the path to the Helm chart directory + type: string + valuesFiles: + description: ValuesFiles is a list of values file names + to use + items: + type: string + type: array + required: + - name + - path + type: object + type: array + dependsOn: + description: |- + DependsOn is a list of package source dependencies + For example: "cozystack.networking" + items: + type: string + type: array + libraries: + description: Libraries is a list of Helm library charts used + by components in this variant + items: + description: Library defines a Helm library chart + properties: + name: + description: Name is the optional name for library placed + in charts + type: string + path: + description: Path is the path to the library chart directory + type: string + required: + - path + type: object + type: array + name: + description: Name is the unique identifier for this variant + type: string + required: + - name + type: object + type: array + type: object + status: + description: PackageSourceStatus defines the observed state of PackageSource + properties: + conditions: + description: Conditions represents the latest available observations + of a PackageSource's state + 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 + variants: + description: |- + Variants is a comma-separated list of package variant names + This field is populated by the controller based on spec.variants keys + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/core/installer/example/platform.yaml b/packages/core/installer/example/platform.yaml new file mode 100644 index 00000000..6ec544aa --- /dev/null +++ b/packages/core/installer/example/platform.yaml @@ -0,0 +1,20 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.cozystack-platform +spec: + variant: isp-full + components: + platform: + values: + publishing: + host: "dev5.infra.aenix.org" + apiServerEndpoint: "https://api.dev5.infra.aenix.org" + externalIPs: + - 10.4.0.94 + - 10.4.0.179 + - 10.4.0.26 + 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..8c4cb79c --- /dev/null +++ b/packages/core/installer/images/cozystack-operator/Dockerfile @@ -0,0 +1,23 @@ +FROM golang:1.25-alpine as builder + +ARG TARGETOS +ARG TARGETARCH + +RUN apk add --no-cache make git + +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 606505ba..00000000 --- a/packages/core/installer/images/cozystack/Dockerfile +++ /dev/null @@ -1,41 +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 - -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 openssl - -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=k8s-await-election-builder /k8s-await-election /usr/bin/k8s-await-election - -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-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml new file mode 100644 index 00000000..25deb21b --- /dev/null +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -0,0 +1,117 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: cozy-system + labels: + cozystack.io/system: "true" + pod-security.kubernetes.io/enforce: privileged +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cozystack + namespace: cozy-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cozystack +subjects: +- kind: ServiceAccount + name: cozystack + namespace: cozy-system +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cozystack-operator + namespace: cozy-system +spec: + replicas: 1 + selector: + matchLabels: + app: cozystack-operator + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + template: + metadata: + labels: + app: cozystack-operator + spec: + serviceAccountName: cozystack + containers: + - 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 }} + - --platform-source-name=cozystack-platform + - --platform-source={{ .Values.cozystackOperator.platformSource }} + env: + - name: KUBERNETES_SERVICE_HOST + value: localhost + - name: KUBERNETES_SERVICE_PORT + value: "7445" + hostNetwork: true + tolerations: + - key: "node.kubernetes.io/not-ready" + operator: "Exists" + effect: "NoSchedule" + - key: "node.cilium.io/agent-not-ready" + operator: "Exists" + effect: "NoSchedule" +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.cozystack-platform +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + components: + - install: + namespace: cozy-system + releaseName: cozystack-platform + name: cozystack-platform + path: core/platform + valuesFiles: + - values.yaml + - name: isp-full + components: + - install: + namespace: cozy-system + releaseName: cozystack-platform + name: cozystack-platform + path: core/platform + valuesFiles: + - values.yaml + - values-isp-full.yaml + - name: isp-hosted + components: + - install: + namespace: cozy-system + releaseName: cozystack-platform + name: cozystack-platform + path: core/platform + valuesFiles: + - values.yaml + - values-isp-hosted.yaml diff --git a/packages/core/installer/templates/cozystack.yaml b/packages/core/installer/templates/cozystack.yaml deleted file mode 100644 index c43b0425..00000000 --- a/packages/core/installer/templates/cozystack.yaml +++ /dev/null @@ -1,79 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: cozy-system - labels: - cozystack.io/system: "true" - pod-security.kubernetes.io/enforce: privileged ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cozystack - namespace: cozy-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: cozystack -subjects: -- kind: ServiceAccount - name: cozystack - namespace: cozy-system -roleRef: - kind: ClusterRole - name: cluster-admin - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cozystack - namespace: cozy-system -spec: - replicas: 1 - selector: - matchLabels: - app: cozystack - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 - template: - metadata: - labels: - app: cozystack - spec: - hostNetwork: true - serviceAccountName: cozystack - containers: - - name: cozystack - image: "{{ .Values.cozystack.image }}" - env: - - name: KUBERNETES_SERVICE_HOST - value: localhost - - name: INSTALL_FLUX - value: "true" - - 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 - tolerations: - - key: "node.kubernetes.io/not-ready" - operator: "Exists" - effect: "NoSchedule" - - key: "node.cilium.io/agent-not-ready" - operator: "Exists" - effect: "NoSchedule" diff --git a/packages/core/installer/templates/crds.yaml b/packages/core/installer/templates/crds.yaml new file mode 100644 index 00000000..9ff44d13 --- /dev/null +++ b/packages/core/installer/templates/crds.yaml @@ -0,0 +1,6 @@ +{{/* +{{- 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..5287ff67 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,2 +1,6 @@ cozystack: image: ghcr.io/cozystack/cozystack/installer:v0.38.2@sha256:9ff92b655de6f9bea3cba4cd42dcffabd9aace6966dcfb1cc02dda2420ea4a15 +cozystackOperator: + image: ghcr.io/cozystack/cozystack/cozystack-operator:latest@sha256:f7f6e0fd9e896b7bfa642d0bfa4378bc14e646bc5c2e86e2e09a82770ef33181 + platformSource: 'oci://ghcr.io/cozystack/cozystack/platform-packages@sha256:0576491291b33936cdf770a5c5b5692add97339c1505fc67a92df9d69dfbfdf6' + cozystackVersion: latest diff --git a/packages/core/platform/Makefile b/packages/core/platform/Makefile index 5c0b4786..41b41eb7 100644 --- a/packages/core/platform/Makefile +++ b/packages/core/platform/Makefile @@ -1,34 +1,30 @@ -NAME=platform +NAME=cozystack-platform NAMESPACE=cozy-system include ../../../scripts/common-envs.mk +include ../../../scripts/package.mk -show: - cozypkg show -n $(NAMESPACE) $(NAME) --plain +image: image-migrations -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 - -reconcile: apply - -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- - -image: image-assets -image-assets: - docker buildx build -f images/cozystack-assets/Dockerfile ../../.. \ - --tag $(REGISTRY)/cozystack-assets:$(call settag,$(TAG)) \ - --cache-from type=registry,ref=$(REGISTRY)/cozystack-assets:latest \ +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/cozystack-assets.json \ + --metadata-file images/migrations.json \ $(BUILDX_ARGS) - IMAGE="$(REGISTRY)/cozystack-assets:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/cozystack-assets.json -o json -r)" \ - yq -i '.assets.image = strenv(IMAGE)' values.yaml - rm -f images/cozystack-assets.json + 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 + +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/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/packages/core/platform/images/migrations/migrations/1 b/packages/core/platform/images/migrations/migrations/1 new file mode 100755 index 00000000..d2be8ce2 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/1 @@ -0,0 +1,25 @@ +#!/bin/sh +# Migration 1 --> 2 + +# Fix mariadb-operator secrets +if kubectl get -n cozy-mariadb-operator secret/mariadb-operator-webhook-cert; then + kubectl annotate -n cozy-mariadb-operator secret/mariadb-operator-webhook-cert meta.helm.sh/release-namespace=cozy-mariadb-operator meta.helm.sh/release-name=mariadb-operator + kubectl label -n cozy-mariadb-operator secret/mariadb-operator-webhook-cert app.kubernetes.io/managed-by=Helm +fi + +# Gratefully remove fluxcd release and keep resources +if kubectl get hr -n cozy-fluxcd cozy-fluxcd 2>/dev/null; then + kubectl patch hr -n cozy-fluxcd cozy-fluxcd -p '{"spec": {"suspend": true}, "metadata": {"finalizers": null}}' --type=merge + kubectl delete hr -n cozy-fluxcd cozy-fluxcd +fi +kubectl delete secret -n cozy-fluxcd -l name=fluxcd + +# Fix kubeovn crds +kubeovn_crds=$(kubectl get crd -o name | grep '\.kubeovn\.io$') +if [ -n "$kubeovn_crds" ]; then + kubectl annotate $kubeovn_crds meta.helm.sh/release-namespace=cozy-kubeovn meta.helm.sh/release-name=kubeovn + kubectl label $kubeovn_crds app.kubernetes.io/managed-by=Helm +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=2 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/10 b/packages/core/platform/images/migrations/migrations/10 new file mode 100755 index 00000000..993e592c --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/10 @@ -0,0 +1,15 @@ +#!/bin/sh +# Migration 10 --> 11 + +# Force reconcile hr keycloak-configure +if kubectl get helmrelease keycloak-configure -n cozy-keycloak; then + kubectl delete po -l app=source-controller -n cozy-fluxcd + timestamp=$(date --rfc-3339=ns) + kubectl annotate helmrelease keycloak-configure -n cozy-keycloak \ + reconcile.fluxcd.io/forceAt="$timestamp" \ + reconcile.fluxcd.io/requestedAt="$timestamp" \ + --overwrite +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=11 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/11 b/packages/core/platform/images/migrations/migrations/11 new file mode 100755 index 00000000..b16c7b4b --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/11 @@ -0,0 +1,21 @@ +#!/bin/sh +# Migration 11 --> 12 + +# Recreate daemonset kube-rbac-proxy + +if kubectl get daemonset kube-rbac-proxy -n cozy-monitoring; then + kubectl delete daemonset kube-rbac-proxy --cascade=orphan -n cozy-monitoring +fi + +if kubectl get helmrelease monitoring-agents -n cozy-monitoring; then + timestamp=$(date --rfc-3339=ns) + kubectl annotate helmrelease monitoring-agents -n cozy-monitoring \ + reconcile.fluxcd.io/forceAt="$timestamp" \ + reconcile.fluxcd.io/requestedAt="$timestamp" \ + --overwrite +fi + +kubectl delete pods -l app.kubernetes.io/component=kube-rbac-proxy -n cozy-monitoring + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=12 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/12 b/packages/core/platform/images/migrations/migrations/12 new file mode 100755 index 00000000..fcb951bc --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/12 @@ -0,0 +1,35 @@ +#!/bin/sh +# Migration 12 --> 13 + +# Copy configuration from ingress to cozystack configmap +if kubectl get hr -n tenant-root tenant-root > /dev/null; then + expose_services=$( + kubectl get hr -n tenant-root ingress -o go-template='{{ with .spec }}{{ with .values }}{{ if .dashboard }}dashboard,{{ end }}{{ if .cdiUploadProxy }}cdi-uploadproxy,{{ end }}{{ if .virtExportProxy }}vm-exportproxy,{{ end }}{{ end }}{{ end }}' + ) + expose_services=$(echo "$expose_services" | awk '{sub(/,$/,""); print}') + + expose_external_ips=$( + kubectl get hr -n tenant-root ingress -o go-template='{{ with .spec }}{{ with .values }}{{ if .externalIPs }}{{ range .externalIPs }}{{ . }},{{ end }}{{ end }}{{ end }}{{ end }}' + ) + expose_external_ips=$(echo "$expose_external_ips" | awk '{sub(/,$/,""); print}') + + existing_expose_external_ips=$(kubectl get cm -n cozy-system cozystack -o go-template='{{ index .data "expose-external-ips" }}') + existing_expose_services=$(kubectl get cm -n cozy-system cozystack -o go-template='{{ index .data "expose-services" }}') + + if [ "$existing_expose_external_ips" == "" ]; then + kubectl patch cm -n cozy-system cozystack --type merge -p="{\"data\":{\"expose-external-ips\":\"$expose_external_ips\"}}" + fi + + if [ "$existing_expose_services" == "" ]; then + kubectl patch cm -n cozy-system cozystack --type merge -p="{\"data\":{\"expose-services\":\"$expose_services\"}}" + fi + + kubectl patch hr -n tenant-root ingress --type json -p='[{"op": "remove", "path": "/spec/values/dashboard"}]' || true + kubectl patch hr -n tenant-root ingress --type json -p='[{"op": "remove", "path": "/spec/values/cdiUploadProxy"}]' || true + kubectl patch hr -n tenant-root ingress --type json -p='[{"op": "remove", "path": "/spec/values/virtExportProxy"}]' || true + kubectl patch hr -n tenant-root ingress --type json -p='[{"op": "remove", "path": "/spec/values/externalIPs"}]' || true + kubectl patch hr -n tenant-root ingress --type merge -p='{"spec":{"chart":{"spec":{"version":"1.6.0"}}}}' +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=13 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/13 b/packages/core/platform/images/migrations/migrations/13 new file mode 100755 index 00000000..203aa29e --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/13 @@ -0,0 +1,10 @@ +#!/bin/sh +# Migration 13 --> 14 + +# Upgrade tenants.apps to new chart version +kubectl get tenants.apps.cozystack.io -A --no-headers --output=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name' | while read NAMESPACE NAME; do + kubectl patch tenants.apps.cozystack.io -n "$NAMESPACE" "$NAME" --type merge -p '{"appVersion":"1.10.0"}' +done + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=14 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/14 b/packages/core/platform/images/migrations/migrations/14 new file mode 100755 index 00000000..95735bb8 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/14 @@ -0,0 +1,10 @@ +#!/bin/sh +# Migration 14 --> 15 + +# Delete the `capi-providers` HelmRelease in the `cozy-cluster-api` namespace if present +if kubectl get hr -n cozy-cluster-api capi-providers >/dev/null 2>&1; then + kubectl delete hr -n cozy-cluster-api capi-providers +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=15 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/15 b/packages/core/platform/images/migrations/migrations/15 new file mode 100755 index 00000000..e0d78cfb --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/15 @@ -0,0 +1,28 @@ +#!/bin/sh +# Migration 15 --> 16 + +if kubectl get validatingwebhookconfigurations.admissionregistration.k8s.io kamaji-validating-webhook-configuration; then + kubectl delete validatingwebhookconfigurations.admissionregistration.k8s.io kamaji-validating-webhook-configuration +fi +kubectl get kamajicontrolplane -A -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,VERSION:.spec.version,VERSION:.status.version --no-headers | + while read namespace name version status; do + if [ "$status" = "v1.32.4" ]; then + continue + fi + (set -x; kubectl patch kamajicontrolplane "$name" -n "$namespace" --type merge -p '{"spec":{"version":"1.32.4"}}') + (set -x; kubectl patch kamajicontrolplane "$name" -n "$namespace" --type merge -p '{"status":{"version":"v1.32.4"}}' --subresource status) + (set -x; kubectl patch tcp "$name" -n "$namespace" --type merge -p '{"spec":{"kubernetes":{"version":"1.32.4"}}}') + (set -x; kubectl patch tcp "$name" -n "$namespace" --type merge -p '{"status":{"kubernetesResources":{"version":{"version":"v1.32.4"}}}} ' --subresource status) + done + +# Upgrade kubernetes.apps to new chart version +kubectl get kuberneteses.apps.cozystack.io -A --no-headers --output=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name' | while read NAMESPACE NAME; do + kubectl patch kuberneteses.apps.cozystack.io -n "$NAMESPACE" "$NAME" --type merge -p '{"appVersion":"0.24.0"}' +done + +if kubectl get helmrelease kamaji -n cozy-kamaji; then + cozypkg reconcile kamaji -n cozy-kamaji --force +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=16 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/16 b/packages/core/platform/images/migrations/migrations/16 new file mode 100755 index 00000000..781e1a26 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/16 @@ -0,0 +1,95 @@ +#!/bin/sh +# Migration 16 --> 17 +# +# fix-nested-resources-map.sh – prints kubectl patch commands. +# * Replaces each resources section with {cpu,memory} merged from requests + limits +# * Adds/Replaces .appVersion with "*" + +set -e + +CRDS=' +clickhouses.apps.cozystack.io +etcds.apps.cozystack.io +ferretdb.apps.cozystack.io +httpcaches.apps.cozystack.io +kafkas.apps.cozystack.io +kuberneteses.apps.cozystack.io +monitorings.apps.cozystack.io +mysqls.apps.cozystack.io +natses.apps.cozystack.io +postgreses.apps.cozystack.io +rabbitmqs.apps.cozystack.io +redises.apps.cozystack.io +seaweedfses.apps.cozystack.io +tcpbalancers.apps.cozystack.io +virtualmachines.apps.cozystack.io +vminstances.apps.cozystack.io +vpns.apps.cozystack.io +' + +for KIND in $CRDS; do + kubectl get "$KIND" -A -o json | jq -r --arg kind "$KIND" ' + .items[] + | . as $obj + | ($obj.metadata.namespace // "") as $ns # namespace (empty string for cluster-scoped) + | $obj.metadata.name as $name # object name + + # ------------------------------------------------------------------------- + # Build an array with every JSON path ending with "resources" + # ------------------------------------------------------------------------- + | [ $obj + | paths + | select(.[-1] == "resources") + ] as $rpaths + + # ------------------------------------------------------------------------- + # Iterate through each resources path + # ------------------------------------------------------------------------- + | foreach $rpaths[] as $rpath (null; + # Current resources object + ($obj | getpath($rpath)) as $res + + # requests + limits merged; requests override limits on key collision + | ($res.requests? // {}) as $req + | ($res.limits? // {}) as $lim + | ($req + $lim) as $flat + + # Keep cpu & memory only + | ($flat + | with_entries(select(.key|test("^(cpu|memory)$"))) + ) as $value + | select(($value|length) > 0) # skip if nothing to patch + + # --------------------------------------------------------------------- + # RFC6901-encoded JSON Pointer to the resources section + # --------------------------------------------------------------------- + | ("/" + ($rpath + | map( + tostring + | gsub("~";"~0") + | gsub("/";"~1") + ) + | join("/") + ) + ) as $pointer + + # --------------------------------------------------------------------- + # Compose JSON Patch: 1) add/replace appVersion, 2) replace resources + # --------------------------------------------------------------------- + | [ + { op:"add", path:"/appVersion", value:"*" }, + { op:"replace", path:$pointer, value:$value } + ] as $patch + + # --------------------------------------------------------------------- + # Print one ready-to-run kubectl patch command + # --------------------------------------------------------------------- + | "kubectl " + + (if $ns != "" then "-n \($ns) " else "" end) + + "patch \($kind) \($name) --type=json -p '\''\($patch|tojson)'\''" + ) + ' | sh -ex +done + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=17 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/17 b/packages/core/platform/images/migrations/migrations/17 new file mode 100755 index 00000000..9749c0e2 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/17 @@ -0,0 +1,10 @@ +#!/bin/sh +# Migration 17 --> 18 + +# Upgrade kubernetes.apps to new chart version +kubectl get kuberneteses.apps.cozystack.io -A --no-headers --output=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name' | while read NAMESPACE NAME; do + kubectl patch kuberneteses.apps.cozystack.io -n "$NAMESPACE" "$NAME" --type merge -p '{"appVersion":"0.26.1"}' +done + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=18 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/18 b/packages/core/platform/images/migrations/migrations/18 new file mode 100755 index 00000000..4a166fab --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/18 @@ -0,0 +1,18 @@ +#!/bin/sh +# Migration 18 --> 19 + +# Upgrade tenants.apps to new chart version +kubectl get tenants.apps.cozystack.io -A --no-headers --output=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name' | while read NAMESPACE NAME; do + kubectl patch tenants.apps.cozystack.io -n "$NAMESPACE" "$NAME" --type merge -p '{"appVersion":"1.13.0"}' +done + +# Upgrade virtualmachines.apps to new chart version +kubectl get virtualmachines.apps.cozystack.io -A --no-headers --output=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name' | while read NAMESPACE NAME; do + kubectl patch virtualmachines.apps.cozystack.io -n "$NAMESPACE" "$NAME" --type merge -p '{"appVersion":"0.14.0"}' +done +kubectl get vminstances.apps.cozystack.io -A --no-headers --output=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name' | while read NAMESPACE NAME; do + kubectl patch vminstances.apps.cozystack.io -n "$NAMESPACE" "$NAME" --type merge -p '{"appVersion":"0.12.0"}' +done + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=19 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/19 b/packages/core/platform/images/migrations/migrations/19 new file mode 100755 index 00000000..446e10ad --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/19 @@ -0,0 +1,53 @@ +#!/bin/sh +# Migration 19 --> 20 + +set -euo pipefail + +kubectl get helmreleases.helm.toolkit.fluxcd.io -A \ + --field-selector=metadata.name=seaweedfs -o json | jq -r ' + .items[] as $o + | ($o.metadata.namespace // "") as $ns + | $o.metadata.name as $name + | $o.spec as $s + | $o.spec.values as $v + + | ( + ($s.chart.spec.version? // "") != "0.7.0" + or ($v.size? != null) + or ($v.storageClass? != null) + or ($v.replicas? != null) + or ($v.zones? != null) + ) as $needsChange + | select($needsChange) + + # JSON Patch + | [ + (if $s.chart.spec.version? then + {op:"replace", path:"/spec/chart/spec/version", value:"0.7.0"} + else + {op:"add", path:"/spec/chart/spec/version", value:"0.7.0"} + end), + + (if ($v|type) != "object" then {op:"add", path:"/spec/values", value:{}} else empty end), + + (if ($v.volume?|type) != "object" then {op:"add", path:"/spec/values/volume", value:{}} else empty end), + + (if $v.size? then {op:"add", path:"/spec/values/volume/size", value:$v.size} else empty end), + (if $v.storageClass? then {op:"add", path:"/spec/values/volume/storageClass", value:$v.storageClass} else empty end), + (if $v.replicas? then {op:"add", path:"/spec/values/volume/replicas", value:$v.replicas} else empty end), + (if $v.zones? then {op:"add", path:"/spec/values/volume/zones", value:$v.zones} else empty end), + + (if $v.size? then {op:"remove", path:"/spec/values/size"} else empty end), + (if $v.storageClass? then {op:"remove", path:"/spec/values/storageClass"} else empty end), + (if $v.replicas? then {op:"remove", path:"/spec/values/replicas"} else empty end), + (if $v.zones? then {op:"remove", path:"/spec/values/zones"} else empty end) + ] as $patch + + | "kubectl " + + (if $ns != "" then "-n \($ns) " else "" end) + + "patch helmreleases.helm.toolkit.fluxcd.io \($name) --type=json -p '\''\($patch|tojson)'\''" +' | sh -ex + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=20 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/2 b/packages/core/platform/images/migrations/migrations/2 new file mode 100755 index 00000000..a3bfd118 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/2 @@ -0,0 +1,16 @@ +#!/bin/sh +# Migration 2 --> 3 + +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$') +if [ -n "$mariadb_crds" ]; then + kubectl annotate $mariadb_crds meta.helm.sh/release-namespace=cozy-mariadb-operator meta.helm.sh/release-name=mariadb-operator + kubectl label $mariadb_crds app.kubernetes.io/managed-by=Helm +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=3 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/20 b/packages/core/platform/images/migrations/migrations/20 new file mode 100755 index 00000000..0122c054 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/20 @@ -0,0 +1,56 @@ +#!/bin/sh +# Migration 20 --> 21 + +set -euo pipefail + +kubectl -n cozy-system delete helmrelease cozystack-api \ + || kubectl -n cozy-system delete deployment cozystack-api --ignore-not-found # fallback to help migration progress if it failed first time +while [ $( kubectl -n cozy-system get po -l app=cozystack-api --no-headers | wc -l ) != 0 ] ; +do + echo "Waiting for Cozystack API pods to be deleted"; + sleep 1 +done + +kubectl -n cozy-system delete helmrelease cozystack-controller \ + || kubectl -n cozy-system delete deployment cozystack-controller --ignore-not-found # same fallback +kubectl delete customresourcedefinitions.apiextensions.k8s.io cozystackresourcedefinitions.cozystack.io --ignore-not-found +while [ $( kubectl -n cozy-system get po -l app=cozystack-controller --no-headers | wc -l ) != 0 ] ; +do + echo "Waiting for Cozystack controller pods to be deleted"; + sleep 1 +done + +kubectl delete helmrelease -n cozy-dashboard dashboard --ignore-not-found +sleep 5 +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 + +sleep 5 +echo "Running lineage-webhook test" +kubectl delete ns cozy-lineage-webhook-test --ignore-not-found && kubectl create ns cozy-lineage-webhook-test +cleanup_test_ns() { + kubectl delete ns cozy-lineage-webhook-test --ignore-not-found +} +trap cleanup_test_ns ERR +timeout 60 sh -c 'until kubectl -n cozy-lineage-webhook-test create service clusterip lineage-webhook-test --clusterip="None" --dry-run=server; do sleep 1; done' +cleanup_test_ns + +timestamp=$(date --rfc-3339=ns || date) +kubectl get namespace -o custom-columns=NAME:.metadata.name --no-headers | + grep '^tenant-' | + while read namespace ; do + (set -x; \ + kubectl annotate \ + pods,services,pvc,secrets,ingresses.networking.k8s.io,workloadmonitors.cozystack.io \ + -n "$namespace" --all \ + migration.cozystack.io="$timestamp" --overwrite || true) + done + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=21 --dry-run=client -o yaml | kubectl apply -f- 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/packages/core/platform/images/migrations/migrations/3 b/packages/core/platform/images/migrations/migrations/3 new file mode 100755 index 00000000..008d6604 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/3 @@ -0,0 +1,12 @@ +#!/bin/sh +# Migration 3 --> 4 + +# Fix kubeovn crds +kubeovn_crds=$(kubectl get crd -o name | grep '\.kubeovn\.io$') +if [ -n "$kubeovn_crds" ]; then + kubectl annotate $kubeovn_crds meta.helm.sh/release-namespace=cozy-kubeovn meta.helm.sh/release-name=kubeovn + kubectl label $kubeovn_crds app.kubernetes.io/managed-by=Helm +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=4 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/4 b/packages/core/platform/images/migrations/migrations/4 new file mode 100755 index 00000000..14603dbb --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/4 @@ -0,0 +1,15 @@ +#!/bin/sh +# Migration 4 --> 5 + +# Fix tenant-kubernetes PVCs +kubectl get secret -l kamaji.clastix.io/project=kamaji,kamaji.clastix.io/component=admin-kubeconfig -A --output=go-template='{{ range .items }}{{ printf "%s %s %s %s\n" .metadata.namespace .metadata.name (index .metadata.labels "kamaji.clastix.io/name") (index .data "super-admin.conf") }}{{ end }}' | while read NAMESPACE NAME CLUSTER CONFIGB64; do + config=$(mktemp) + echo "$CONFIGB64" | base64 -d > "$config" + kubectl get pv --kubeconfig="$config" --output=go-template='{{ range .items }}{{ printf "%s\n" .metadata.name }}{{ end }}' | while read PVC; do + (set -x; kubectl label pvc --overwrite -n "$NAMESPACE" "$PVC" "cluster.x-k8s.io/cluster-name=$CLUSTER") + done + rm -f "$config" +done + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=5 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/5 b/packages/core/platform/images/migrations/migrations/5 new file mode 100755 index 00000000..d1abc6ea --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/5 @@ -0,0 +1,15 @@ +#!/bin/sh +# Migration 5 --> 6 + +# Fix tenant-kubernetes PVCs +kubectl get secret -l kamaji.clastix.io/project=kamaji,kamaji.clastix.io/component=admin-kubeconfig -A --output=go-template='{{ range .items }}{{ printf "%s %s %s %s\n" .metadata.namespace .metadata.name (index .metadata.labels "kamaji.clastix.io/name") (index .data "super-admin.conf") }}{{ end }}' | while read NAMESPACE NAME CLUSTER CONFIGB64; do + config=$(mktemp) + echo "$CONFIGB64" | base64 -d > "$config" + kubectl get pv --kubeconfig="$config" --output=go-template='{{ range .items }}{{ printf "%s\n" .metadata.name }}{{ end }}' | while read PVC; do + (set -x; kubectl label pvc --overwrite -n "$NAMESPACE" "$PVC" "cluster.x-k8s.io/cluster-name=$CLUSTER") + (set -x; kubectl label dv --overwrite -n "$NAMESPACE" "$PVC" "cluster.x-k8s.io/cluster-name=$CLUSTER") + done + rm -f "$config" +done + +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=6 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/6 b/packages/core/platform/images/migrations/migrations/6 new file mode 100755 index 00000000..b5486da9 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/6 @@ -0,0 +1,16 @@ +#!/bin/sh +# Migration 6 --> 7 + +# Delete cert-manager crds labels and annotations +kubectl patch hr -n cozy-cert-manager cert-manager -p '{"spec": {"suspend": true}}' --type=merge --field-manager=flux-client-side-apply +certmanager_crds=$(kubectl get crd -o name | grep '\.cert-manager\.io$') +if [ -n "$certmanager_crds" ]; then + kubectl annotate $certmanager_crds meta.helm.sh/release-namespace=cozy-cert-manager meta.helm.sh/release-name=cert-manager-crds + kubectl label $certmanager_crds app.kubernetes.io/managed-by=Helm +fi + +# Remove monitoring, because it is renamed to monitoring-agents +kubectl get hr -n cozy-monitoring monitoring && kubectl delete hr -n cozy-monitoring monitoring --wait=0 + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=7 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/7 b/packages/core/platform/images/migrations/migrations/7 new file mode 100755 index 00000000..2becfce4 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/7 @@ -0,0 +1,8 @@ +#!/bin/sh +# Migration 7 --> 8 + +host=$(kubectl get hr tenant-root -n tenant-root -o yaml | grep 'host:' | awk '{print $2}') +kubectl patch configmap -n cozy-system cozystack --type merge -p "{\"data\":{\"root-host\":\"$host\"}}" + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=8 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/8 b/packages/core/platform/images/migrations/migrations/8 new file mode 100755 index 00000000..c7d7350c --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/8 @@ -0,0 +1,9 @@ +#!/bin/sh +# Migration 8 --> 9 + +if kubectl get clusterrolebinding kubeapps-admin-group; then + kubectl delete clusterrolebinding kubeapps-admin-group +fi + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=9 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/images/migrations/migrations/9 b/packages/core/platform/images/migrations/migrations/9 new file mode 100755 index 00000000..cf6643af --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/9 @@ -0,0 +1,10 @@ +#!/bin/sh +# Migration 9 --> 10 + +# Upgrade kubernetes.apps to new chart version +kubectl get kuberneteses.apps.cozystack.io -A --no-headers --output=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name' | while read NAMESPACE NAME; do + kubectl patch kuberneteses.apps.cozystack.io -n "$NAMESPACE" "$NAME" --type merge -p '{"appVersion":"0.15.1"}' +done + +# Write version to cozystack-version config +kubectl create configmap -n cozy-system cozystack-version --from-literal=version=10 --dry-run=client -o yaml | kubectl apply -f- 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 deleted file mode 100644 index c656c7f8..00000000 --- a/packages/core/platform/templates/_helpers.tpl +++ /dev/null @@ -1,108 +0,0 @@ -{{/* -Get IP-addresses of master nodes -*/}} -{{- define "cozystack.master-node-ips" -}} -{{- $nodes := lookup "v1" "Node" "" "" -}} -{{- $ips := list -}} -{{- range $node := $nodes.items -}} - {{- if eq (index $node.metadata.labels "node-role.kubernetes.io/control-plane") "" -}} - {{- range $address := $node.status.addresses -}} - {{- if eq $address.type "InternalIP" -}} - {{- $ips = append $ips $address.address -}} - {{- break -}} - {{- end -}} - {{- end -}} - {{- end -}} -{{- end -}} -{{ join "," $ips }} -{{- end -}} - -{{/* -Get Kubernetes API Endpoint from cozystack deployment -Returns host:port format -*/}} -{{- define "cozystack.kubernetesAPIEndpoint" -}} -{{- $cozyDeployment := lookup "apps/v1" "Deployment" "cozy-system" "cozystack" }} -{{- $cozyContainers := dig "spec" "template" "spec" "containers" list $cozyDeployment }} -{{- $kubernetesServiceHost := "" }} -{{- $kubernetesServicePort := "" }} -{{- range $cozyContainers }} -{{- if eq .name "cozystack" }} -{{- range .env }} -{{- if eq .name "KUBERNETES_SERVICE_HOST" }} -{{- $kubernetesServiceHost = .value }} -{{- end }} -{{- if eq .name "KUBERNETES_SERVICE_PORT" }} -{{- $kubernetesServicePort = .value }} -{{- end }} -{{- end }} -{{- end }} -{{- end }} -{{- if eq $kubernetesServiceHost "" }} -{{- $kubernetesServiceHost = "kubernetes.default.svc" }} -{{- end }} -{{- if eq $kubernetesServicePort "" }} -{{- $kubernetesServicePort = "443" }} -{{- end }} -{{- printf "%s:%s" $kubernetesServiceHost $kubernetesServicePort }} -{{- 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 }} - {{- 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 }} 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/config.yaml b/packages/core/platform/templates/config.yaml new file mode 100644 index 00000000..6dff8c30 --- /dev/null +++ b/packages/core/platform/templates/config.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cozystack + namespace: cozy-system +data: + root-host: {{ .Values.publishing.host | quote }} + api-server-endpoint: {{ .Values.publishing.apiServerEndpoint | quote }} + expose-services: {{ (join "," .Values.publishing.exposedServices) | quote }} + expose-ingress: {{ .Values.publishing.ingressName | quote }} + expose-external-ips: {{ (join "," .Values.publishing.externalIPs) | quote }} + cluster-domain: {{ .Values.networking.clusterDomain | quote }} + ipv4-pod-cidr: {{ .Values.networking.podCIDR | quote }} + ipv4-pod-gateway: {{ .Values.networking.podGateway | quote }} + ipv4-svc-cidr: {{ .Values.networking.serviceCIDR | quote }} + ipv4-join-cidr: {{ .Values.networking.joinCIDR | quote }} + clusterissuer: {{ .Values.publishing.certificates.issuerType | quote }} + oidc-enabled: {{ .Values.authentication.oidc.enabled | toString | quote }} + extra-keycloak-redirect-uri-for-dashboard: {{ (join "," .Values.authentication.oidc.dashboard.extraRedirectUris) | quote }} + cpu-allocation-ratio: {{ .Values.resources.cpuAllocationRatio | toString | quote }} + memory-allocation-ratio: {{ .Values.resources.memoryAllocationRatio | toString | quote }} + ephemeral-storage-allocation-ratio: {{ .Values.resources.ephemeralStorageAllocationRatio | toString | quote }} + management-kubeconfig-endpoint: {{ .Values.publishing.apiServerEndpoint | quote }} \ No newline at end of file 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/cozystack-assets.yaml b/packages/core/platform/templates/cozystack-assets.yaml deleted file mode 100644 index 61ab8dca..00000000 --- a/packages/core/platform/templates/cozystack-assets.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: cozystack-assets - namespace: cozy-system - labels: - app: cozystack-assets -spec: - serviceName: cozystack-assets - replicas: 1 - selector: - matchLabels: - app: cozystack-assets - template: - metadata: - labels: - app: cozystack-assets - spec: - hostNetwork: true - containers: - - name: assets-server - image: "{{ .Values.assets.image }}" - args: - - "-dir=/cozystack/assets" - - "-address=:8123" - ports: - - name: http - containerPort: 8123 - hostPort: 8123 - tolerations: - - operator: Exists ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: cozystack-assets-reader - namespace: cozy-system -rules: - - apiGroups: [""] - resources: - - pods/proxy - resourceNames: - - cozystack-assets-0 - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: cozystack-assets-reader - namespace: cozy-system -subjects: - - kind: User - name: cozystack-assets-reader - apiGroup: rbac.authorization.k8s.io -roleRef: - kind: Role - name: cozystack-assets-reader - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: v1 -kind: Service -metadata: - name: cozystack-assets - namespace: cozy-system -spec: - ports: - - name: http - port: 80 - targetPort: 8123 - selector: - app: cozystack-assets - type: ClusterIP 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 47954869..00000000 --- a/packages/core/platform/templates/helmrepos.yaml +++ /dev/null @@ -1,40 +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: https://{{ include "cozystack.kubernetesAPIEndpoint" . }}/api/v1/namespaces/cozy-system/pods/cozystack-assets-0/proxy/repos/system - certSecretRef: - name: cozystack-assets-tls ---- -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: https://{{ include "cozystack.kubernetesAPIEndpoint" . }}/api/v1/namespaces/cozy-system/pods/cozystack-assets-0/proxy/repos/apps - certSecretRef: - name: cozystack-assets-tls ---- -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: cozystack-extra - namespace: cozy-public - labels: - cozystack.io/repository: extra -spec: - interval: 5m0s - url: https://{{ include "cozystack.kubernetesAPIEndpoint" . }}/api/v1/namespaces/cozy-system/pods/cozystack-assets-0/proxy/repos/extra - certSecretRef: - name: cozystack-assets-tls 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/packages/isp-full.yaml b/packages/core/platform/templates/packages/isp-full.yaml new file mode 100644 index 00000000..d2e5c237 --- /dev/null +++ b/packages/core/platform/templates/packages/isp-full.yaml @@ -0,0 +1,215 @@ +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.networking +spec: + variant: kubeovn-cilium + components: + kubeovn: + values: + kube-ovn: + ipv4: + POD_CIDR: "{{ .Values.networking.podCIDR }}" + POD_GATEWAY: "{{ .Values.networking.podGateway }}" + SVC_CIDR: "{{ .Values.networking.serviceCIDR }}" + JOIN_CIDR: "{{ .Values.networking.joinCIDR }}" +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.cozystack-engine +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.cert-manager +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.kubeovn-webhook +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.kubeovn-plunger +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.victoria-metrics-operator +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.cozy-proxy +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.multus +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.tenant-application +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.ingress-application +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.seaweedfs-application +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.info-application +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.monitoring-application +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.etcd-application +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.cozystack-basics +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.backup-controller +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.velero +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.vertical-pod-autoscaler +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.metallb +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.gpu-operator +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.reloader +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.linstor +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.monitoring-agents +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.goldpinger +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.snapshot-controller +spec: + variant: default + components: {} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.prometheus-operator-crds +spec: + variant: default + components: {} diff --git a/packages/core/platform/templates/repository.yaml b/packages/core/platform/templates/repository.yaml new file mode 100644 index 00000000..9bd9f5a1 --- /dev/null +++ b/packages/core/platform/templates/repository.yaml @@ -0,0 +1,17 @@ +{{- $sourceRef := .Values.sourceRef }} +{{- $apiVersion := "source.toolkit.fluxcd.io/v1" }} +{{- $kind := $sourceRef.kind }} +{{- $name := $sourceRef.name }} +{{- $namespace := $sourceRef.namespace }} +{{- $sourceRepo := lookup $apiVersion $kind $namespace $name }} +{{- if not $sourceRepo }} +{{- fail (printf "Source repository %s/%s of kind %s not found in namespace %s" $namespace $name $kind $namespace) }} +{{- end }} +--- +apiVersion: {{ $apiVersion }} +kind: {{ $kind }} +metadata: + name: cozystack-packages + namespace: cozy-system +spec: +{{- $sourceRepo.spec | toYaml | nindent 2 }} diff --git a/packages/core/platform/templates/scheduling.yaml b/packages/core/platform/templates/scheduling.yaml new file mode 100644 index 00000000..4486155a --- /dev/null +++ b/packages/core/platform/templates/scheduling.yaml @@ -0,0 +1,11 @@ +{{- if .Values.scheduling.globalAppTopologySpreadConstraints }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cozystack-scheduling + namespace: cozy-system +data: + globalAppTopologySpreadConstraints: | + {{- toYaml .Values.scheduling.globalAppTopologySpreadConstraints | nindent 4 }} +{{- end }} diff --git a/packages/core/platform/templates/sources/backup-controller.yaml b/packages/core/platform/templates/sources/backup-controller.yaml new file mode 100644 index 00000000..f68e168b --- /dev/null +++ b/packages/core/platform/templates/sources/backup-controller.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.backup-controller +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: backup-controller + path: system/backup-controller + install: + privileged: true + namespace: cozy-backup-controller + releaseName: backup-controller diff --git a/packages/core/platform/templates/sources/bootbox-application.yaml b/packages/core/platform/templates/sources/bootbox-application.yaml new file mode 100644 index 00000000..a1c8a481 --- /dev/null +++ b/packages/core/platform/templates/sources/bootbox-application.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.bootbox-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: bootbox-system + path: system/bootbox + install: + releaseName: bootbox-application + namespace: cozy-system + - name: bootbox + path: extra/bootbox + libraries: ["cozy-lib"] + - name: bootbox-rd + path: system/bootbox-rd + install: + namespace: cozy-system + releaseName: bootbox-rd diff --git a/packages/core/platform/templates/sources/bootbox.yaml b/packages/core/platform/templates/sources/bootbox.yaml new file mode 100644 index 00000000..a7ce246e --- /dev/null +++ b/packages/core/platform/templates/sources/bootbox.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.bootbox +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.bootbox-application + components: + - name: bootbox + path: system/bootbox + install: + privileged: true + namespace: cozy-bootbox + releaseName: bootbox diff --git a/packages/core/platform/templates/sources/bucket-application.yaml b/packages/core/platform/templates/sources/bucket-application.yaml new file mode 100644 index 00000000..a50c116f --- /dev/null +++ b/packages/core/platform/templates/sources/bucket-application.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.bucket-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.objectstorage-controller + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: bucket-system + path: system/bucket + - name: bucket + path: apps/bucket + libraries: ["cozy-lib"] + - name: bucket-rd + path: system/bucket-rd + install: + namespace: cozy-system + releaseName: bucket-rd diff --git a/packages/core/platform/templates/sources/capi-operator.yaml b/packages/core/platform/templates/sources/capi-operator.yaml new file mode 100644 index 00000000..a4536402 --- /dev/null +++ b/packages/core/platform/templates/sources/capi-operator.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.capi-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: capi-operator + path: system/capi-operator + install: + privileged: true + namespace: cozy-cluster-api + releaseName: capi-operator diff --git a/packages/core/platform/templates/sources/capi-providers-bootstrap.yaml b/packages/core/platform/templates/sources/capi-providers-bootstrap.yaml new file mode 100644 index 00000000..04946a16 --- /dev/null +++ b/packages/core/platform/templates/sources/capi-providers-bootstrap.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.capi-providers-bootstrap +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: kubevirt + dependsOn: + - cozystack.networking + - cozystack.capi-operator + components: + - name: capi-providers-bootstrap + path: system/capi-providers-bootstrap + install: + privileged: true + namespace: cozy-cluster-api + releaseName: capi-providers-bootstrap + dependsOn: + - capi-operator diff --git a/packages/core/platform/templates/sources/capi-providers-core.yaml b/packages/core/platform/templates/sources/capi-providers-core.yaml new file mode 100644 index 00000000..e3a4e8c3 --- /dev/null +++ b/packages/core/platform/templates/sources/capi-providers-core.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.capi-providers-core +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.capi-operator + components: + - name: capi-providers-core + path: system/capi-providers-core + install: + privileged: true + namespace: cozy-cluster-api + releaseName: capi-providers-core + dependsOn: + - capi-operator diff --git a/packages/core/platform/templates/sources/capi-providers-cpprovider.yaml b/packages/core/platform/templates/sources/capi-providers-cpprovider.yaml new file mode 100644 index 00000000..026494fe --- /dev/null +++ b/packages/core/platform/templates/sources/capi-providers-cpprovider.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.capi-providers-cpprovider +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: kamaji + dependsOn: + - cozystack.networking + - cozystack.capi-operator + - cozystack.kamaji + components: + - name: capi-providers-cpprovider + path: system/capi-providers-cpprovider + install: + privileged: true + namespace: cozy-cluster-api + releaseName: capi-providers-cpprovider + dependsOn: + - capi-operator diff --git a/packages/core/platform/templates/sources/capi-providers-infraprovider.yaml b/packages/core/platform/templates/sources/capi-providers-infraprovider.yaml new file mode 100644 index 00000000..22cf0c32 --- /dev/null +++ b/packages/core/platform/templates/sources/capi-providers-infraprovider.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.capi-providers-infraprovider +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: kubevirt + dependsOn: + - cozystack.networking + - cozystack.capi-operator + - cozystack.kubevirt + components: + - name: capi-providers-infraprovider + path: system/capi-providers-infraprovider + install: + privileged: true + namespace: cozy-cluster-api + releaseName: capi-providers-infraprovider + dependsOn: + - capi-operator diff --git a/packages/core/platform/templates/sources/cert-manager.yaml b/packages/core/platform/templates/sources/cert-manager.yaml new file mode 100644 index 00000000..0907021e --- /dev/null +++ b/packages/core/platform/templates/sources/cert-manager.yaml @@ -0,0 +1,35 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.cert-manager +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: cert-manager-crds + path: system/cert-manager-crds + install: + namespace: cozy-cert-manager + releaseName: cert-manager-crds + - name: cert-manager + path: system/cert-manager + install: + namespace: cozy-cert-manager + releaseName: cert-manager + dependsOn: + - cert-manager-crds + - name: cert-manager-issuers + path: system/cert-manager-issuers + install: + namespace: cozy-cert-manager + releaseName: cert-manager-issuers + dependsOn: + - cert-manager diff --git a/packages/core/platform/templates/sources/clickhouse-application.yaml b/packages/core/platform/templates/sources/clickhouse-application.yaml new file mode 100644 index 00000000..f22a003d --- /dev/null +++ b/packages/core/platform/templates/sources/clickhouse-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.clickhouse-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.clickhouse-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: clickhouse + path: apps/clickhouse + libraries: ["cozy-lib"] + - name: clickhouse-rd + path: system/clickhouse-rd + install: + namespace: cozy-system + releaseName: clickhouse-rd diff --git a/packages/core/platform/templates/sources/clickhouse-operator.yaml b/packages/core/platform/templates/sources/clickhouse-operator.yaml new file mode 100644 index 00000000..c49ff9ab --- /dev/null +++ b/packages/core/platform/templates/sources/clickhouse-operator.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.clickhouse-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.monitoring + - cozystack.cozystack-core + components: + - name: clickhouse-operator + path: system/clickhouse-operator + install: + namespace: cozy-clickhouse-operator + releaseName: clickhouse-operator diff --git a/packages/core/platform/templates/sources/cozy-proxy.yaml b/packages/core/platform/templates/sources/cozy-proxy.yaml new file mode 100644 index 00000000..fac0f5fc --- /dev/null +++ b/packages/core/platform/templates/sources/cozy-proxy.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.cozy-proxy +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: cozy-proxy + path: system/cozy-proxy + install: + namespace: cozy-system + releaseName: cozystack diff --git a/packages/core/platform/templates/sources/cozystack-basics.yaml b/packages/core/platform/templates/sources/cozystack-basics.yaml new file mode 100644 index 00000000..a3fbbad1 --- /dev/null +++ b/packages/core/platform/templates/sources/cozystack-basics.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.cozystack-basics +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.tenant-application + components: + - name: cozystack-basics + path: system/cozystack-basics + install: + namespace: cozy-system + releaseName: cozystack-basics diff --git a/packages/core/platform/templates/sources/cozystack-engine.yaml b/packages/core/platform/templates/sources/cozystack-engine.yaml new file mode 100644 index 00000000..ce06b9d8 --- /dev/null +++ b/packages/core/platform/templates/sources/cozystack-engine.yaml @@ -0,0 +1,108 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.cozystack-engine +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: cozystack-resource-definition-crd + path: system/cozystack-resource-definition-crd + install: + namespace: cozy-system + releaseName: cozystack-resource-definition-crd + - name: cozystack-controller + path: system/cozystack-controller + install: + namespace: cozy-system + releaseName: cozystack-controller + dependsOn: + - cozystack-resource-definition-crd + - name: cozystack-api + path: system/cozystack-api + install: + namespace: cozy-system + releaseName: cozystack-api + dependsOn: + - cozystack-controller + - cozystack-resource-definition-crd + - name: lineage-controller-webhook + path: system/lineage-controller-webhook + install: + namespace: cozy-system + releaseName: lineage-controller-webhook + dependsOn: + - cozystack-controller + - cozystack-resource-definition-crd + - name: dashboard + path: system/dashboard + install: + namespace: cozy-dashboard + releaseName: dashboard + dependsOn: + - cozystack-api + - cozystack-controller + - cozystack-resource-definition-crd + - name: oidc + dependsOn: + - cozystack.networking + - cozystack.keycloak + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: cozystack-resource-definition-crd + path: system/cozystack-resource-definition-crd + install: + namespace: cozy-system + releaseName: cozystack-resource-definition-crd + - name: cozystack-controller + path: system/cozystack-controller + install: + namespace: cozy-system + releaseName: cozystack-controller + dependsOn: + - cozystack-resource-definition-crd + - name: cozystack-api + path: system/cozystack-api + install: + namespace: cozy-system + releaseName: cozystack-api + dependsOn: + - cozystack-controller + - cozystack-resource-definition-crd + - name: lineage-controller-webhook + path: system/lineage-controller-webhook + install: + namespace: cozy-system + releaseName: lineage-controller-webhook + dependsOn: + - cozystack-controller + - cozystack-resource-definition-crd + - name: dashboard + path: system/dashboard + install: + namespace: cozy-dashboard + releaseName: dashboard + dependsOn: + - cozystack-api + - cozystack-controller + - keycloak-configure + - cozystack-resource-definition-crd + - name: keycloak-configure + path: system/keycloak-configure + install: + namespace: cozy-keycloak + releaseName: keycloak-configure + dependsOn: [] diff --git a/packages/core/platform/templates/sources/etcd-application.yaml b/packages/core/platform/templates/sources/etcd-application.yaml new file mode 100644 index 00000000..1abcec12 --- /dev/null +++ b/packages/core/platform/templates/sources/etcd-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.etcd-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: etcd + path: extra/etcd + libraries: ["cozy-lib"] + - name: etcd-rd + path: system/etcd-rd + install: + namespace: cozy-system + releaseName: etcd-rd diff --git a/packages/core/platform/templates/sources/etcd-operator.yaml b/packages/core/platform/templates/sources/etcd-operator.yaml new file mode 100644 index 00000000..f9c49d49 --- /dev/null +++ b/packages/core/platform/templates/sources/etcd-operator.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.etcd-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.cert-manager + components: + - name: etcd-operator + path: system/etcd-operator + install: + namespace: cozy-etcd-operator + releaseName: etcd-operator + dependsOn: + - cert-manager diff --git a/packages/core/platform/templates/sources/external-dns.yaml b/packages/core/platform/templates/sources/external-dns.yaml new file mode 100644 index 00000000..a3f6ca6e --- /dev/null +++ b/packages/core/platform/templates/sources/external-dns.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.external-dns +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: external-dns + path: system/external-dns + install: + namespace: cozy-external-dns + releaseName: external-dns diff --git a/packages/core/platform/templates/sources/external-secrets-operator.yaml b/packages/core/platform/templates/sources/external-secrets-operator.yaml new file mode 100644 index 00000000..bc69dcce --- /dev/null +++ b/packages/core/platform/templates/sources/external-secrets-operator.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.external-secrets-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: external-secrets-operator + path: system/external-secrets-operator + install: + namespace: cozy-external-secrets-operator + releaseName: external-secrets-operator diff --git a/packages/core/platform/templates/sources/ferretdb-application.yaml b/packages/core/platform/templates/sources/ferretdb-application.yaml new file mode 100644 index 00000000..447745a5 --- /dev/null +++ b/packages/core/platform/templates/sources/ferretdb-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.ferretdb-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: ferretdb + path: apps/ferretdb + libraries: [cozy-lib] + - name: ferretdb-rd + path: system/ferretdb-rd + install: + namespace: cozy-system + releaseName: ferretdb-rd diff --git a/packages/core/platform/templates/sources/foundationdb-application.yaml b/packages/core/platform/templates/sources/foundationdb-application.yaml new file mode 100644 index 00000000..4488b033 --- /dev/null +++ b/packages/core/platform/templates/sources/foundationdb-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.foundationdb-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.foundationdb-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: foundationdb + path: apps/foundationdb + libraries: ["cozy-lib"] + - name: foundationdb-rd + path: system/foundationdb-rd + install: + namespace: cozy-system + releaseName: foundationdb-rd diff --git a/packages/core/platform/templates/sources/foundationdb-operator.yaml b/packages/core/platform/templates/sources/foundationdb-operator.yaml new file mode 100644 index 00000000..2f88dd06 --- /dev/null +++ b/packages/core/platform/templates/sources/foundationdb-operator.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.foundationdb-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.cert-manager + components: + - name: foundationdb-operator + path: system/foundationdb-operator + install: + namespace: cozy-foundationdb-operator + releaseName: foundationdb-operator + dependsOn: + - cert-manager diff --git a/packages/core/platform/templates/sources/goldpinger.yaml b/packages/core/platform/templates/sources/goldpinger.yaml new file mode 100644 index 00000000..975ad138 --- /dev/null +++ b/packages/core/platform/templates/sources/goldpinger.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.goldpinger +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.prometheus-operator-crds + components: + - name: goldpinger + path: system/goldpinger + install: + privileged: true + namespace: cozy-goldpinger + releaseName: goldpinger diff --git a/packages/core/platform/templates/sources/gpu-operator.yaml b/packages/core/platform/templates/sources/gpu-operator.yaml new file mode 100644 index 00000000..e41cc9b2 --- /dev/null +++ b/packages/core/platform/templates/sources/gpu-operator.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.gpu-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: gpu-operator + path: system/gpu-operator + valuesFiles: + - values.yaml + - values-talos.yaml + install: + privileged: true + namespace: cozy-gpu-operator + releaseName: gpu-operator diff --git a/packages/core/platform/templates/sources/grafana-operator.yaml b/packages/core/platform/templates/sources/grafana-operator.yaml new file mode 100644 index 00000000..d46597b7 --- /dev/null +++ b/packages/core/platform/templates/sources/grafana-operator.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.grafana-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: grafana-operator + path: system/grafana-operator + install: + namespace: cozy-grafana-operator + releaseName: grafana-operator diff --git a/packages/core/platform/templates/sources/hetzner-robotlb.yaml b/packages/core/platform/templates/sources/hetzner-robotlb.yaml new file mode 100644 index 00000000..8660890c --- /dev/null +++ b/packages/core/platform/templates/sources/hetzner-robotlb.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.hetzner-robotlb +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: hetzner-robotlb + path: system/hetzner-robotlb + install: + namespace: cozy-hetzner-robotlb + releaseName: robotlb diff --git a/packages/core/platform/templates/sources/http-cache-application.yaml b/packages/core/platform/templates/sources/http-cache-application.yaml new file mode 100644 index 00000000..67537b4f --- /dev/null +++ b/packages/core/platform/templates/sources/http-cache-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.http-cache-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: http-cache + path: apps/http-cache + libraries: [cozy-lib] + - name: http-cache-rd + path: system/http-cache-rd + install: + namespace: cozy-system + releaseName: http-cache-rd diff --git a/packages/core/platform/templates/sources/info-application.yaml b/packages/core/platform/templates/sources/info-application.yaml new file mode 100644 index 00000000..25b8b798 --- /dev/null +++ b/packages/core/platform/templates/sources/info-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.info-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: info + path: extra/info + libraries: ["cozy-lib"] + - name: info-rd + path: system/info-rd + install: + namespace: cozy-system + releaseName: info-rd diff --git a/packages/core/platform/templates/sources/ingress-application.yaml b/packages/core/platform/templates/sources/ingress-application.yaml new file mode 100644 index 00000000..638c2f1c --- /dev/null +++ b/packages/core/platform/templates/sources/ingress-application.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.ingress-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: ingress-system + path: system/ingress-nginx + - name: ingress + path: extra/ingress + libraries: ["cozy-lib"] + - name: ingress-rd + path: system/ingress-rd + install: + namespace: cozy-system + releaseName: ingress-rd diff --git a/packages/core/platform/templates/sources/kafka-application.yaml b/packages/core/platform/templates/sources/kafka-application.yaml new file mode 100644 index 00000000..4a5ca158 --- /dev/null +++ b/packages/core/platform/templates/sources/kafka-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.kafka-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.kafka-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: kafka + path: apps/kafka + libraries: ["cozy-lib"] + - name: kafka-rd + path: system/kafka-rd + install: + namespace: cozy-system + releaseName: kafka-rd diff --git a/packages/core/platform/templates/sources/kafka-operator.yaml b/packages/core/platform/templates/sources/kafka-operator.yaml new file mode 100644 index 00000000..c930160b --- /dev/null +++ b/packages/core/platform/templates/sources/kafka-operator.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.kafka-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.monitoring + components: + - name: kafka-operator + path: system/kafka-operator + install: + namespace: cozy-kafka-operator + releaseName: kafka-operator + dependsOn: + - victoria-metrics-operator diff --git a/packages/core/platform/templates/sources/kamaji.yaml b/packages/core/platform/templates/sources/kamaji.yaml new file mode 100644 index 00000000..915c5898 --- /dev/null +++ b/packages/core/platform/templates/sources/kamaji.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.kamaji +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: kamaji + path: system/kamaji + install: + namespace: cozy-kamaji + releaseName: kamaji diff --git a/packages/core/platform/templates/sources/keycloak-operator.yaml b/packages/core/platform/templates/sources/keycloak-operator.yaml new file mode 100644 index 00000000..0c10921f --- /dev/null +++ b/packages/core/platform/templates/sources/keycloak-operator.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.keycloak-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: keycloak-operator + path: system/keycloak-operator + install: + namespace: cozy-keycloak + releaseName: keycloak-operator + dependsOn: + - keycloak diff --git a/packages/core/platform/templates/sources/keycloak.yaml b/packages/core/platform/templates/sources/keycloak.yaml new file mode 100644 index 00000000..a9dc64b4 --- /dev/null +++ b/packages/core/platform/templates/sources/keycloak.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.keycloak +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.postgres-operator + components: + - name: keycloak + path: system/keycloak + install: + namespace: cozy-keycloak + releaseName: keycloak diff --git a/packages/core/platform/templates/sources/kubeovn-plunger.yaml b/packages/core/platform/templates/sources/kubeovn-plunger.yaml new file mode 100644 index 00000000..bdd8f1c8 --- /dev/null +++ b/packages/core/platform/templates/sources/kubeovn-plunger.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.kubeovn-plunger +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.cert-manager + - cozystack.networking + - cozystack.victoria-metrics-operator + components: + - name: kubeovn-plunger + path: system/kubeovn-plunger + install: + namespace: cozy-kubeovn + releaseName: kubeovn-plunger diff --git a/packages/core/platform/templates/sources/kubeovn-webhook.yaml b/packages/core/platform/templates/sources/kubeovn-webhook.yaml new file mode 100644 index 00000000..3125b692 --- /dev/null +++ b/packages/core/platform/templates/sources/kubeovn-webhook.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.kubeovn-webhook +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.cert-manager + components: + - name: kubeovn-webhook + path: system/kubeovn-webhook + install: + privileged: true + namespace: cozy-kubeovn + releaseName: kubeovn-webhook diff --git a/packages/core/platform/templates/sources/kubernetes-application.yaml b/packages/core/platform/templates/sources/kubernetes-application.yaml new file mode 100644 index 00000000..0acde1c4 --- /dev/null +++ b/packages/core/platform/templates/sources/kubernetes-application.yaml @@ -0,0 +1,68 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.kubernetes-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: kubevirt + dependsOn: + - cozystack.networking + - cozystack.capi-operator + - cozystack.capi-providers-bootstrap + - cozystack.capi-providers-core + - cozystack.capi-providers-cpprovider + - cozystack.capi-providers-infraprovider + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - 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: kubernetes-prometheus-operator-crds + path: system/prometheus-operator-crds + - name: kubernetes-metrics-server + path: system/metrics-server + - name: kubernetes + path: apps/kubernetes + libraries: ["cozy-lib"] + - name: kubernetes-rd + path: system/kubernetes-rd + install: + namespace: cozy-system + releaseName: kubernetes-rd diff --git a/packages/core/platform/templates/sources/kubevirt-cdi.yaml b/packages/core/platform/templates/sources/kubevirt-cdi.yaml new file mode 100644 index 00000000..bd925c0d --- /dev/null +++ b/packages/core/platform/templates/sources/kubevirt-cdi.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.kubevirt-cdi +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: kubevirt-cdi-operator + path: system/kubevirt-cdi-operator + install: + namespace: cozy-kubevirt-cdi + releaseName: kubevirt-cdi-operator + - name: kubevirt-cdi + path: system/kubevirt-cdi + install: + namespace: cozy-kubevirt-cdi + releaseName: kubevirt-cdi + dependsOn: + - kubevirt-cdi-operator diff --git a/packages/core/platform/templates/sources/kubevirt.yaml b/packages/core/platform/templates/sources/kubevirt.yaml new file mode 100644 index 00000000..3b41854b --- /dev/null +++ b/packages/core/platform/templates/sources/kubevirt.yaml @@ -0,0 +1,40 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.kubevirt +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.victoria-metrics-operator + components: + - name: kubevirt-operator + path: system/kubevirt-operator + install: + namespace: cozy-kubevirt + releaseName: kubevirt-operator + dependsOn: + - victoria-metrics-operator + - name: kubevirt + path: system/kubevirt + install: + privileged: true + namespace: cozy-kubevirt + releaseName: kubevirt + dependsOn: + - kubevirt-operator + - name: kubevirt-instancetypes + path: system/kubevirt-instancetypes + install: + namespace: cozy-kubevirt + releaseName: kubevirt-instancetypes + dependsOn: + - kubevirt-operator + - kubevirt diff --git a/packages/core/platform/templates/sources/linstor.yaml b/packages/core/platform/templates/sources/linstor.yaml new file mode 100644 index 00000000..f440affe --- /dev/null +++ b/packages/core/platform/templates/sources/linstor.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.linstor +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.cert-manager + - cozystack.victoria-metrics-operator + - cozystack.reloader + - cozystack.snapshot-controller + components: + - name: piraeus-operator + path: system/piraeus-operator + install: + namespace: cozy-linstor + releaseName: piraeus-operator + - name: linstor + path: system/linstor + install: + privileged: true + namespace: cozy-linstor + releaseName: linstor + dependsOn: + - piraeus-operator diff --git a/packages/core/platform/templates/sources/mariadb-operator.yaml b/packages/core/platform/templates/sources/mariadb-operator.yaml new file mode 100644 index 00000000..fe509e7c --- /dev/null +++ b/packages/core/platform/templates/sources/mariadb-operator.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.mariadb-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.monitoring + - cozystack.cert-manager + components: + - name: mariadb-operator + path: system/mariadb-operator + install: + namespace: cozy-mariadb-operator + releaseName: mariadb-operator + dependsOn: + - cert-manager + - victoria-metrics-operator diff --git a/packages/core/platform/templates/sources/metallb.yaml b/packages/core/platform/templates/sources/metallb.yaml new file mode 100644 index 00000000..e1a09886 --- /dev/null +++ b/packages/core/platform/templates/sources/metallb.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.metallb +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: metallb + path: system/metallb + install: + privileged: true + namespace: cozy-metallb + releaseName: metallb diff --git a/packages/core/platform/templates/sources/monitoring-agents.yaml b/packages/core/platform/templates/sources/monitoring-agents.yaml new file mode 100644 index 00000000..9585c5aa --- /dev/null +++ b/packages/core/platform/templates/sources/monitoring-agents.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.monitoring-agents +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.victoria-metrics-operator + - cozystack.vertical-pod-autoscaler + components: + - name: monitoring-agents + path: system/monitoring-agents + install: + releaseName: monitoring-agents + namespace: cozy-monitoring + privileged: true diff --git a/packages/core/platform/templates/sources/monitoring-application.yaml b/packages/core/platform/templates/sources/monitoring-application.yaml new file mode 100644 index 00000000..0f01eddd --- /dev/null +++ b/packages/core/platform/templates/sources/monitoring-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.monitoring-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: monitoring + path: extra/monitoring + libraries: ["cozy-lib"] + - name: monitoring-rd + path: system/monitoring-rd + install: + namespace: cozy-system + releaseName: monitoring-rd diff --git a/packages/core/platform/templates/sources/multus.yaml b/packages/core/platform/templates/sources/multus.yaml new file mode 100644 index 00000000..88910e93 --- /dev/null +++ b/packages/core/platform/templates/sources/multus.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.multus +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: multus + path: system/multus + install: + privileged: true + namespace: cozy-multus + releaseName: multus diff --git a/packages/core/platform/templates/sources/mysql-application.yaml b/packages/core/platform/templates/sources/mysql-application.yaml new file mode 100644 index 00000000..4b22e36e --- /dev/null +++ b/packages/core/platform/templates/sources/mysql-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.mysql-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.mariadb-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: mysql + path: apps/mysql + libraries: ["cozy-lib"] + - name: mysql-rd + path: system/mysql-rd + install: + namespace: cozy-system + releaseName: mysql-rd diff --git a/packages/core/platform/templates/sources/nats-application.yaml b/packages/core/platform/templates/sources/nats-application.yaml new file mode 100644 index 00000000..b09c5a30 --- /dev/null +++ b/packages/core/platform/templates/sources/nats-application.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.nats-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: nats-system + path: system/nats + - name: nats + path: apps/nats + libraries: ["cozy-lib"] + - name: nats-rd + path: system/nats-rd + install: + namespace: cozy-system + releaseName: nats-rd diff --git a/packages/core/platform/templates/sources/networking.yaml b/packages/core/platform/templates/sources/networking.yaml new file mode 100644 index 00000000..57fff6fc --- /dev/null +++ b/packages/core/platform/templates/sources/networking.yaml @@ -0,0 +1,65 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.networking +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: noop + components: [] + - name: cilium + dependsOn: [] + components: + - name: cilium + path: system/cilium + valuesFiles: + - values.yaml + - values-talos.yaml + install: + privileged: true + namespace: cozy-cilium + releaseName: cilium + dependsOn: [] + - name: cilium-networkpolicy + path: system/cilium-networkpolicy + install: + privileged: true + namespace: cozy-cilium + releaseName: cilium-networkpolicy + dependsOn: + - cilium + - name: kubeovn-cilium + dependsOn: [] + components: + - name: cilium + path: system/cilium + valuesFiles: + - values.yaml + - values-talos.yaml + - values-kubeovn.yaml + install: + privileged: true + namespace: cozy-cilium + releaseName: cilium + dependsOn: [] + - name: cilium-networkpolicy + path: system/cilium-networkpolicy + install: + privileged: true + namespace: cozy-cilium + releaseName: cilium-networkpolicy + dependsOn: + - cilium + - name: kubeovn + path: system/kubeovn + install: + privileged: true + namespace: cozy-kubeovn + releaseName: kubeovn + dependsOn: + - cilium diff --git a/packages/core/platform/templates/sources/nfs-driver.yaml b/packages/core/platform/templates/sources/nfs-driver.yaml new file mode 100644 index 00000000..aa1effec --- /dev/null +++ b/packages/core/platform/templates/sources/nfs-driver.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.nfs-driver +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: nfs-driver + path: system/nfs-driver + install: + privileged: true + namespace: cozy-nfs-driver + releaseName: nfs-driver diff --git a/packages/core/platform/templates/sources/objectstorage-controller.yaml b/packages/core/platform/templates/sources/objectstorage-controller.yaml new file mode 100644 index 00000000..cf89ee46 --- /dev/null +++ b/packages/core/platform/templates/sources/objectstorage-controller.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.objectstorage-controller +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: objectstorage-controller + path: system/objectstorage-controller + install: + namespace: cozy-objectstorage-controller + releaseName: objectstorage-controller diff --git a/packages/core/platform/templates/sources/postgres-application.yaml b/packages/core/platform/templates/sources/postgres-application.yaml new file mode 100644 index 00000000..b5953069 --- /dev/null +++ b/packages/core/platform/templates/sources/postgres-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.postgres-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: postgres + path: apps/postgres + libraries: ["cozy-lib"] + - name: postgres-rd + path: system/postgres-rd + install: + namespace: cozy-system + releaseName: postgres-rd diff --git a/packages/core/platform/templates/sources/postgres-operator.yaml b/packages/core/platform/templates/sources/postgres-operator.yaml new file mode 100644 index 00000000..158ab5a2 --- /dev/null +++ b/packages/core/platform/templates/sources/postgres-operator.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.postgres-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.monitoring + - cozystack.cert-manager + components: + - name: postgres-operator + path: system/postgres-operator + install: + namespace: cozy-postgres-operator + releaseName: postgres-operator + dependsOn: + - cert-manager + - victoria-metrics-operator diff --git a/packages/core/platform/templates/sources/prometheus-operator-crds.yaml b/packages/core/platform/templates/sources/prometheus-operator-crds.yaml new file mode 100644 index 00000000..4ab9ad98 --- /dev/null +++ b/packages/core/platform/templates/sources/prometheus-operator-crds.yaml @@ -0,0 +1,20 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.prometheus-operator-crds +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: [] + components: + - name: prometheus-operator-crds + path: system/prometheus-operator-crds + install: + namespace: cozy-victoria-metrics-operator + releaseName: prometheus-operator-crds diff --git a/packages/core/platform/templates/sources/rabbitmq-application.yaml b/packages/core/platform/templates/sources/rabbitmq-application.yaml new file mode 100644 index 00000000..7dc68b31 --- /dev/null +++ b/packages/core/platform/templates/sources/rabbitmq-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.rabbitmq-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.rabbitmq-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: rabbitmq + path: apps/rabbitmq + libraries: ["cozy-lib"] + - name: rabbitmq-rd + path: system/rabbitmq-rd + install: + namespace: cozy-system + releaseName: rabbitmq-rd diff --git a/packages/core/platform/templates/sources/rabbitmq-operator.yaml b/packages/core/platform/templates/sources/rabbitmq-operator.yaml new file mode 100644 index 00000000..843ceb47 --- /dev/null +++ b/packages/core/platform/templates/sources/rabbitmq-operator.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.rabbitmq-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: rabbitmq-operator + path: system/rabbitmq-operator + install: + namespace: cozy-rabbitmq-operator + releaseName: rabbitmq-operator diff --git a/packages/core/platform/templates/sources/redis-application.yaml b/packages/core/platform/templates/sources/redis-application.yaml new file mode 100644 index 00000000..41a2a9dc --- /dev/null +++ b/packages/core/platform/templates/sources/redis-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.redis-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.redis-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: redis + path: apps/redis + libraries: ["cozy-lib"] + - name: redis-rd + path: system/redis-rd + install: + namespace: cozy-system + releaseName: redis-rd diff --git a/packages/core/platform/templates/sources/redis-operator.yaml b/packages/core/platform/templates/sources/redis-operator.yaml new file mode 100644 index 00000000..9abb3d26 --- /dev/null +++ b/packages/core/platform/templates/sources/redis-operator.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.redis-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: redis-operator + path: system/redis-operator + install: + namespace: cozy-redis-operator + releaseName: redis-operator diff --git a/packages/core/platform/templates/sources/reloader.yaml b/packages/core/platform/templates/sources/reloader.yaml new file mode 100644 index 00000000..fb3f2e44 --- /dev/null +++ b/packages/core/platform/templates/sources/reloader.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.reloader +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: reloader + path: system/reloader + install: + namespace: cozy-reloader + releaseName: reloader diff --git a/packages/core/platform/templates/sources/seaweedfs-application.yaml b/packages/core/platform/templates/sources/seaweedfs-application.yaml new file mode 100644 index 00000000..70de6662 --- /dev/null +++ b/packages/core/platform/templates/sources/seaweedfs-application.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.seaweedfs-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: seaweedfs-system + path: system/seaweedfs + - name: seaweedfs + path: extra/seaweedfs + libraries: ["cozy-lib"] + - name: seaweedfs-rd + path: system/seaweedfs-rd + install: + namespace: cozy-system + releaseName: seaweedfs-rd diff --git a/packages/core/platform/templates/sources/snapshot-controller.yaml b/packages/core/platform/templates/sources/snapshot-controller.yaml new file mode 100644 index 00000000..3d4342a4 --- /dev/null +++ b/packages/core/platform/templates/sources/snapshot-controller.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.snapshot-controller +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.cert-manager + components: + - name: snapshot-controller + path: system/snapshot-controller + install: + namespace: cozy-snapshot-controller + releaseName: snapshot-controller diff --git a/packages/core/platform/templates/sources/tcp-balancer-application.yaml b/packages/core/platform/templates/sources/tcp-balancer-application.yaml new file mode 100644 index 00000000..0ba031e7 --- /dev/null +++ b/packages/core/platform/templates/sources/tcp-balancer-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.tcp-balancer-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: tcp-balancer + path: apps/tcp-balancer + libraries: ["cozy-lib"] + - name: tcp-balancer-rd + path: system/tcp-balancer-rd + install: + namespace: cozy-system + releaseName: tcp-balancer-rd diff --git a/packages/core/platform/templates/sources/telepresence.yaml b/packages/core/platform/templates/sources/telepresence.yaml new file mode 100644 index 00000000..ae52d918 --- /dev/null +++ b/packages/core/platform/templates/sources/telepresence.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.telepresence +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: telepresence + path: system/telepresence + install: + namespace: cozy-telepresence + releaseName: traffic-manager diff --git a/packages/core/platform/templates/sources/tenant-application.yaml b/packages/core/platform/templates/sources/tenant-application.yaml new file mode 100644 index 00000000..d95cfd44 --- /dev/null +++ b/packages/core/platform/templates/sources/tenant-application.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.tenant-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.ingress-application + - cozystack.seaweedfs-application + - cozystack.info-application + - cozystack.monitoring-application + - cozystack.etcd-application + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: tenant + path: apps/tenant + libraries: [cozy-lib] + - name: tenant-rd + path: system/tenant-rd + install: + namespace: cozy-system + releaseName: tenant-rd diff --git a/packages/core/platform/templates/sources/velero.yaml b/packages/core/platform/templates/sources/velero.yaml new file mode 100644 index 00000000..2374e847 --- /dev/null +++ b/packages/core/platform/templates/sources/velero.yaml @@ -0,0 +1,23 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.velero +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.monitoring-agents + components: + - name: velero + path: system/velero + install: + privileged: true + namespace: cozy-velero + releaseName: velero diff --git a/packages/core/platform/templates/sources/vertical-pod-autoscaler.yaml b/packages/core/platform/templates/sources/vertical-pod-autoscaler.yaml new file mode 100644 index 00000000..a4d827cc --- /dev/null +++ b/packages/core/platform/templates/sources/vertical-pod-autoscaler.yaml @@ -0,0 +1,32 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.vertical-pod-autoscaler +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: vertical-pod-autoscaler-crds + path: system/vertical-pod-autoscaler-crds + install: + privileged: true + namespace: cozy-vertical-pod-autoscaler + releaseName: vertical-pod-autoscaler-crds + - name: vpa-for-vpa + path: system/vertical-pod-autoscaler + - name: vertical-pod-autoscaler + path: system/vertical-pod-autoscaler + install: + privileged: true + namespace: cozy-vertical-pod-autoscaler + releaseName: vertical-pod-autoscaler + dependsOn: + - vertical-pod-autoscaler-crds diff --git a/packages/core/platform/templates/sources/victoria-metrics-operator.yaml b/packages/core/platform/templates/sources/victoria-metrics-operator.yaml new file mode 100644 index 00000000..a5b47683 --- /dev/null +++ b/packages/core/platform/templates/sources/victoria-metrics-operator.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.victoria-metrics-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.cert-manager + components: + - name: victoria-metrics-operator + path: system/victoria-metrics-operator + install: + namespace: cozy-victoria-metrics-operator + releaseName: victoria-metrics-operator diff --git a/packages/core/platform/templates/sources/virtual-machine-application.yaml b/packages/core/platform/templates/sources/virtual-machine-application.yaml new file mode 100644 index 00000000..b2583e7e --- /dev/null +++ b/packages/core/platform/templates/sources/virtual-machine-application.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.virtual-machine-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: kubevirt + dependsOn: + - cozystack.networking + - cozystack.kubevirt + - cozystack.kubevirt-cdi + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: virtual-machine + path: apps/virtual-machine + libraries: [cozy-lib] + - name: virtual-machine-rd + path: system/virtual-machine-rd + install: + namespace: cozy-system + releaseName: virtual-machine-rd diff --git a/packages/core/platform/templates/sources/virtualprivatecloud-application.yaml b/packages/core/platform/templates/sources/virtualprivatecloud-application.yaml new file mode 100644 index 00000000..b6a8519b --- /dev/null +++ b/packages/core/platform/templates/sources/virtualprivatecloud-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.virtualprivatecloud-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: kubevirt + dependsOn: + - cozystack.networking + - cozystack.multus + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: virtualprivatecloud + path: apps/vpc + libraries: [cozy-lib] + - name: virtualprivatecloud-rd + path: system/virtualprivatecloud-rd + install: + namespace: cozy-system + releaseName: virtualprivatecloud-rd diff --git a/packages/core/platform/templates/sources/vm-disk-application.yaml b/packages/core/platform/templates/sources/vm-disk-application.yaml new file mode 100644 index 00000000..a4e98ca9 --- /dev/null +++ b/packages/core/platform/templates/sources/vm-disk-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.vm-disk-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: kubevirt + dependsOn: + - cozystack.networking + - cozystack.kubevirt-cdi + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: vm-disk + path: apps/vm-disk + libraries: ["cozy-lib"] + - name: vm-disk-rd + path: system/vm-disk-rd + install: + namespace: cozy-system + releaseName: vm-disk-rd diff --git a/packages/core/platform/templates/sources/vm-instance-application.yaml b/packages/core/platform/templates/sources/vm-instance-application.yaml new file mode 100644 index 00000000..eaa0c76e --- /dev/null +++ b/packages/core/platform/templates/sources/vm-instance-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.vm-instance-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: kubevirt + dependsOn: + - cozystack.networking + - cozystack.kubevirt + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: vm-instance + path: apps/vm-instance + libraries: ["cozy-lib"] + - name: vm-instance-rd + path: system/vm-instance-rd + install: + namespace: cozy-system + releaseName: vm-instance-rd diff --git a/packages/core/platform/templates/sources/vpn-application.yaml b/packages/core/platform/templates/sources/vpn-application.yaml new file mode 100644 index 00000000..0200108f --- /dev/null +++ b/packages/core/platform/templates/sources/vpn-application.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.vpn-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: vpn + path: apps/vpn + libraries: ["cozy-lib"] + - name: vpn-rd + path: system/vpn-rd + install: + namespace: cozy-system + releaseName: vpn-rd 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-isp-full.yaml b/packages/core/platform/values-isp-full.yaml new file mode 100644 index 00000000..af0997bc --- /dev/null +++ b/packages/core/platform/values-isp-full.yaml @@ -0,0 +1,12 @@ +migrations: + enabled: true +bundles: + system: + enabled: true + type: "full" + iaas: + enabled: true + paas: + enabled: true + naas: + enabled: true diff --git a/packages/core/platform/values-isp-hosted.yaml b/packages/core/platform/values-isp-hosted.yaml new file mode 100644 index 00000000..eb20159a --- /dev/null +++ b/packages/core/platform/values-isp-hosted.yaml @@ -0,0 +1,12 @@ +migrations: + enabled: true +bundles: + system: + enabled: true + type: "hosted" + iaas: + enabled: false + paas: + enabled: true + naas: + enabled: true diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 363d1921..22d2f87d 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -1,2 +1,89 @@ -assets: - image: ghcr.io/cozystack/cozystack/cozystack-assets:latest@sha256:19b166819d0205293c85d8351a3e038dc4c146b876a8e2ae21dce1d54f0b9e33 +sourceRef: + kind: OCIRepository + name: cozystack-platform + namespace: cozy-system + path: / +migrations: + enabled: false + image: ghcr.io/cozystack/cozystack/platform-migrations:latest@sha256:390dff1ac16d99c7677a2b580e0dc41bcb2ffd50232ddf4adff81d6f86aae70b + targetVersion: 23 +# Bundle deployment configuration +bundles: + system: + enabled: false + type: "hosted" + iaas: + enabled: true + paas: + enabled: true + naas: + enabled: true +# 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/packages.yaml b/packages/packages.yaml new file mode 100644 index 00000000..3405c21d --- /dev/null +++ b/packages/packages.yaml @@ -0,0 +1,24 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: cozystack.networking +spec: + variant: cilium-kubeovn + #ignoreDependencies: + #- cozystack.networking + components: + cilium: + enabled: false + values: + kubeProxyReplacement: strict + k8sServiceHost: api.example.org + k8sServicePort: 6443 + kubeovn: + values: + kube-ovn: + ipv4: + POD_CIDR: "10.244.0.0/16" + POD_GATEWAY: "10.244.0.1" + SVC_CIDR: "10.96.0.0/16" + JOIN_CIDR: "100.64.0.0/16" diff --git a/packages/system/bootbox-rd/Chart.yaml b/packages/system/bootbox-rd/Chart.yaml new file mode 100644 index 00000000..0a35867d --- /dev/null +++ b/packages/system/bootbox-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: bootbox-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/bootbox-rd/Makefile b/packages/system/bootbox-rd/Makefile new file mode 100644 index 00000000..b570a84f --- /dev/null +++ b/packages/system/bootbox-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=bootbox-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/bootbox-rd/cozyrds/bootbox.yaml b/packages/system/bootbox-rd/cozyrds/bootbox.yaml new file mode 100644 index 00000000..f47bf8f6 --- /dev/null +++ b/packages/system/bootbox-rd/cozyrds/bootbox.yaml @@ -0,0 +1,43 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: bootbox +spec: + application: + kind: BootBox + plural: bootboxes + singular: bootbox + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"machines":{"description":"Configuration of physical machine instances.","type":"array","default":[],"items":{"type":"object","required":["arch","hostname","ip","leaseTime","uefi"],"properties":{"arch":{"description":"Architecture.","type":"string"},"hostname":{"description":"Hostname.","type":"string"},"ip":{"description":"IP address configuration.","type":"object","required":["address","gateway","netmask"],"properties":{"address":{"description":"IP address.","type":"string"},"gateway":{"description":"IP gateway.","type":"string"},"netmask":{"description":"Netmask.","type":"string"}}},"leaseTime":{"description":"Lease time.","type":"integer"},"mac":{"description":"MAC addresses.","type":"array","items":{"type":"string"}},"nameServers":{"description":"Name servers.","type":"array","items":{"type":"string"}},"timeServers":{"description":"Time servers.","type":"array","items":{"type":"string"}},"uefi":{"description":"UEFI.","type":"boolean"}}}},"whitelist":{"description":"List of client networks.","type":"array","default":[],"items":{"type":"string"}},"whitelistHTTP":{"description":"Secure HTTP by enabling client networks whitelisting.","type":"boolean","default":true}}} + release: + prefix: "" + labels: + cozystack.io/ui: "true" + chart: + name: bootbox + sourceRef: + kind: HelmRepository + name: cozystack-extra + namespace: cozy-public + dashboard: + category: Administration + singular: BootBox + plural: BootBox + name: bootbox + description: PXE hardware provisioning + module: true + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl85NzlfNzkyKSIvPgo8cGF0aCBkPSJNNzEuNTY5OCA3Ni41MzM2QzcxLjIzNzQgNzYuNTMzNiA3MC45MDM2IDc2LjQ4NDcgNzAuNTgyOSA3Ni4zODkyTDM2LjIwNzkgNjYuMDc2N0MzNC4zODg1IDY1LjUzMTEgMzMuMzU2MiA2My42MTQ0IDMzLjkwMTcgNjEuNzk2NkMzNC40NDg5IDU5Ljk3NTUgMzYuMzY1NyA1OC45NDgzIDM4LjE4MTcgNTkuNDkwNEw3MS41Njk4IDY5LjUwNzZMMTA0Ljk1OCA1OS40OTA0QzEwNi43NzYgNTguOTYgMTA4LjY5MSA1OS45NzcyIDEwOS4yMzggNjEuNzk2NkMxMDkuNzgzIDYzLjYxNDQgMTA4Ljc1MSA2NS41MzExIDEwNi45MzIgNjYuMDc2N0w3Mi41NTY3IDc2LjM4OTJDNzIuMjM2IDc2LjQ4NDcgNzEuOTAyMiA3Ni41MzM2IDcxLjU2OTggNzYuNTMzNloiIGZpbGw9IiMyMzFGMjAiLz4KPHBhdGggZD0iTTc0Ljk3MyA1My4wMjE0Qzc0Ljc2NjggNTQuMzI3NiA3My44MDQzIDU1LjM5MzMgNzIuNTY2OCA1NS43NzE0TDM4LjE5MTggNjYuMDgzOUMzNy44NDggNjYuMTg3IDM3LjUzODcgNjYuMjIxNCAzNy4xOTQ5IDY2LjIyMTRDMzYuMzY5OSA2Ni4yMjE0IDM1LjU3OTMgNjUuOTEyIDM0LjkyNjIgNjUuMzYyTDIxLjE3NjIgNTMuMzMwOEMyMC4yNDggNTIuNTQwMSAxOS44MzU1IDUxLjI2ODMgMjAuMDc2MiA1MC4wNjUxQzIwLjMxNjggNDguODYyIDIxLjE3NjIgNDcuODY1MSAyMi4zNDQ5IDQ3LjQ4N0w1My4yODI0IDM3LjE3NDVDNTQuMzEzNyAzNi44MzA4IDU1LjQ0OCAzNy4wMDI2IDU2LjM0MTggMzcuNjIxNEw3My41MjkzIDQ5LjY1MjZDNzQuNjI5MyA1MC40MDg5IDc1LjE3OTMgNTEuNzE1MSA3NC45NzMgNTMuMDIxNFoiIGZpbGw9IiNGRkRDODMiLz4KPHBhdGggZD0iTTEyMS45NjQgNTMuMzMwOEwxMDguMjE0IDY1LjM2MkMxMDcuNTYgNjUuOTEyIDEwNi43NyA2Ni4yMjE0IDEwNS45NDUgNjYuMjIxNEMxMDUuNjAxIDY2LjIyMTQgMTA1LjI5MiA2Ni4xODcgMTA0Ljk0OCA2Ni4wODM5TDcwLjU3MyA1NS43NzE0QzY5LjMzNTUgNTUuMzkzMyA2OC4zNzMgNTQuMzI3NiA2OC4xNjY3IDUzLjAyMTRDNjcuOTYwNSA1MS43MTUxIDY4LjUxMDUgNTAuNDA4OSA2OS42MTA1IDQ5LjY1MjZMODYuNzk4IDM3LjYyMTRDODcuNjkxNyAzNy4wMDI2IDg4LjgyNjEgMzYuODMwOCA4OS44NTc0IDM3LjE3NDVMMTIwLjc5NSA0Ny40ODdDMTIxLjk2NCA0Ny44NjUxIDEyMi44MjMgNDguODYyIDEyMy4wNjQgNTAuMDY1MUMxMjMuMzA0IDUxLjI2ODMgMTIyLjg5MiA1Mi41NDAxIDEyMS45NjQgNTMuMzMwOFoiIGZpbGw9IiNGRkRDODMiLz4KPHBhdGggZD0iTTEwOS4zODIgNjMuNjQyNlYxMDcuNDcxQzEwOS4zODIgMTA4Ljg4IDEwOC41MjIgMTEwLjE1MiAxMDcuMjE2IDExMC42NjhMNzIuODQxMiAxMjQuNDE4QzcyLjQyODcgMTI0LjU4OSA3Mi4wMTYyIDEyNC42NTggNzEuNTY5MyAxMjQuNjU4QzcxLjEyMjUgMTI0LjY1OCA3MC43MSAxMjQuNTg5IDcwLjI5NzUgMTI0LjQxOEwzNS45MjI1IDExMC42NjhDMzQuNjE2MiAxMTAuMTUyIDMzLjc1NjggMTA4Ljg4IDMzLjc1NjggMTA3LjQ3MVY2My42NDI2QzMzLjc1NjggNjEuNzUyIDM1LjMwMzcgNjAuMjA1MSAzNy4xOTQzIDYwLjIwNTFIMTA1Ljk0NEMxMDcuODM1IDYwLjIwNTEgMTA5LjM4MiA2MS43NTIgMTA5LjM4MiA2My42NDI2WiIgZmlsbD0iI0VBQkQ0QyIvPgo8cGF0aCBkPSJNMTA3Ljk5OSA2MS40OTU4QzEwNy45OTkgNjIuOTgxMiAxMDcuMDM3IDY0LjI5NzkgMTA1LjY0MyA2NC43MzY4TDcyLjQ2MTMgNzQuODY1QzcyLjEyOTUgNzQuOTY2MiA3MS44MzA4IDc1IDcxLjQ5OSA3NUM3MS4xNjcyIDc1IDcwLjg2ODYgNzQuOTY2MiA3MC41MzY4IDc0Ljg2NUwzNy4zNTQ5IDY0LjczNjhDMzUuOTYxMyA2NC4yOTc5IDM0Ljk5OSA2Mi45ODEyIDM0Ljk5OSA2MS40OTU4QzM0Ljk5OSA2MC4wMTAzIDM1Ljk2MTMgNTguNjkzNyAzNy4zNTQ5IDU4LjI1NDhMNzAuNTM2OCA0OC4xMjY2QzcxLjE2NzIgNDcuOTU3OCA3MS44MzA4IDQ3Ljk1NzggNzIuNDYxMyA0OC4xMjY2TDEwNS42NDMgNTguMjU0OEMxMDcuMDM3IDU4LjY5MzcgMTA3Ljk5OSA2MC4wMTAzIDEwNy45OTkgNjEuNDk1OFoiIGZpbGw9IiM0QzM4MjUiLz4KPHBhdGggZD0iTTc0LjUxMTggNzdDNzUuMzUgNzcgNzYuMTc5NCA3Ni45NjI4IDc3IDc2LjkxMzNWMjEuMDg2N0M3Ni4xNzY1IDIxLjAzNDcgNzUuMzQ3MSAyMSA3NC41MDU5IDIxQzczLjY2NDcgMjEgNzIuODI5NCAyMS4wMzQ3IDcyIDIxLjA4NjdWNzYuOTEwOEM3Mi44MjY1IDc2Ljk2MjggNzMuNjU4OCA3Ni45OTc1IDc0LjUgNzdINzQuNTExOFoiIGZpbGw9InVybCgjcGFpbnQxX2xpbmVhcl85NzlfNzkyKSIvPgo8cGF0aCBkPSJNNDQuMDI4MiAzOC4xMTI5TDQzLjIwNzggMzcuMjk1OUM0Mi4wNzczIDM4LjkxMjEgNDEuMDc0NiA0MC42MTQgNDAuMjA4OCA0Mi4zODYxQzUwLjEwMDEgNTIuNDM1NCA1MS4xNDI0IDU3LjI4MzUgNTEuMTI4OSA1OC45MDc0QzUxLjA5MTkgNjMuMDI2IDQ2LjA1MjIgNjkuNDg0NSA0MC4xNDE2IDc1LjQ2NTdDNDAuOTk5NiA3Ny4yMzc1IDQxLjk5MzMgNzguOTQwNSA0My4xMTM3IDgwLjU1OTJDNDMuNDQ5OSA4MC4yMjMgNDMuNzY5MyA3OS45MTM3IDQ0LjA5NTQgNzkuNTg0MkM1Mi42MjUgNzAuOTk3NSA1Ni43OTQgNjQuMjQ5OCA1Ni44NDQ1IDU4Ljk0NzdDNTYuODk0OSA1My42NDU3IDUyLjcwMjQgNDYuODg3OSA0NC4wMjgyIDM4LjExMjlaIiBmaWxsPSJ1cmwoI3BhaW50Ml9saW5lYXJfOTc5Xzc5MikiLz4KPHBhdGggZD0iTTEwNC42OTUgNzkuNTk3NUwxMDUuNjc2IDgwLjU3MjVDMTA2Ljc5NSA3OC45NDkyIDEwNy43ODcgNzcuMjQxNyAxMDguNjQyIDc1LjQ2NTVDMTAyLjczNSA2OS40NzA5IDk3LjY5NDggNjIuOTk1NSA5Ny42NTQ1IDU4Ljg5MzdDOTcuNjE3NSA1NC44OTI4IDEwMi42MjcgNDguNDIwOCAxMDguNTY4IDQyLjM1OUMxMDcuNzAzIDQwLjU5MTcgMTA2LjcwMyAzOC44OTQ0IDEwNS41NzYgMzcuMjgyMkwxMDQuNzU1IDM4LjA5OTJDOTYuMDgxIDQ2Ljg1NzUgOTEuODg4NSA1My42NzkxIDkxLjkzODkgNTguOTQ0MkM5MS45ODk0IDY0LjIwOTIgOTYuMTU4MyA3MS4wMTA3IDEwNC42OTUgNzkuNTk3NVoiIGZpbGw9InVybCgjcGFpbnQzX2xpbmVhcl85NzlfNzkyKSIvPgo8cGF0aCBkPSJNODcuNDM5NiA1OC45MzQ0Qzg3LjQzOTYgNTEuNTM3OCA5MC43Mzc4IDM5LjIzOTMgOTUuODE3OSAyNy42MTY1Qzk0LjE5NzkgMjYuNTEzOSA5Mi40OTUgMjUuNTM4MiA5MC43MjQ0IDI0LjY5ODJDODUuMTYzNSAzNy4yMjg3IDgxLjcyMDcgNTAuNTU2MSA4MS43MjA3IDU4Ljk0NzhDODEuNzIwNyA2Ny4wNjczIDg1LjQ0OTMgODAuNTQyNSA5MS4xMTEgOTMuMTQwM0M5Mi44NDY4IDkyLjI4NTkgOTQuNTE0NyA5MS4zMDAyIDk2LjEwMDQgOTAuMTkxN0M5MC44NTg5IDc4LjQwMDkgODcuNDM5NiA2Ni4wNzU1IDg3LjQzOTYgNTguOTM0NFoiIGZpbGw9InVybCgjcGFpbnQ0X2xpbmVhcl85NzlfNzkyKSIvPgo8cGF0aCBkPSJNNjcuMDM4NCA1OC45MzUzQzY3LjAzODQgNTAuNDk5OCA2My40NTc4IDM3LjA5ODUgNTcuOTYwOCAyNC43MjI3QzU2LjIxNTggMjUuNTYxMyA1NC41Mzc3IDI2LjUzMjUgNTIuOTQxMiAyNy42Mjc1QzU4LjAzMTQgMzkuMjQzNSA2MS4zMjI4IDUxLjU0NTQgNjEuMzIyOCA1OC45MzUzQzYxLjMyMjggNjYuMDczIDU3LjkwMzYgNzguMzk1IDUyLjY2MjEgOTAuMTcyNEM1NC4yNDgyIDkxLjI4MDIgNTUuOTE2MSA5Mi4yNjU4IDU3LjY1MTQgOTMuMTIxQzYzLjMxOTkgODAuNTI2NiA2Ny4wMzg0IDY3LjA2MTQgNjcuMDM4NCA1OC45MzUzWiIgZmlsbD0idXJsKCNwYWludDVfbGluZWFyXzk3OV83OTIpIi8+CjxwYXRoIGQ9Ik03NC40MjI5IDc0Ljk4N0w2MC42NzI5IDk1LjYxMkM2MC4wMTk3IDk2LjYwODkgNTguOTU0MSA5Ny4xNTg5IDU3LjgxOTcgOTcuMTU4OUM1Ny40MDcyIDk3LjE1ODkgNTYuOTYwNCA5Ny4wOTAxIDU2LjU0NzkgOTYuOTE4M0wyMi4xNzI5IDgzLjE2ODNDMjEuMTQxNiA4Mi43NTU4IDIwLjM4NTQgODEuODk2NCAyMC4xMTA0IDgwLjg2NTFDMTkuODM1NCA3OS43OTk1IDIwLjA3NiA3OC42NjUxIDIwLjc2MzUgNzcuODQwMUwzNC41MTM1IDYwLjY1MjZDMzUuMzcyOSA1OS41NTI2IDMyLjczOTEgNTcuODQwNCAzNC4wNzk3IDU4LjIxODVMNzIuNTY2NiA2OS43OTY0QzczLjU5NzkgNzAuMTA1OCA3NC40MjI5IDcwLjg5NjQgNzQuODAxIDcxLjkyNzZDNzUuMTc5MSA3Mi45NTg5IDc1LjA0MTYgNzQuMDkzMyA3NC40MjI5IDc0Ljk4N1oiIGZpbGw9IiNGRkRDODMiLz4KPHBhdGggZD0iTTEyMy4wMjkgODAuNjI0MkMxMjIuNzU0IDgxLjY1NTUgMTIxLjk5OCA4Mi41NDkyIDEyMS4wMDEgODIuOTI3NEw4Ni42MjYxIDk2LjkxOEM4Ni4xNzkyIDk3LjA4OTkgODUuNzY2NyA5Ny4xNTg2IDg1LjMxOTkgOTcuMTU4NkM4NC4xODU1IDk3LjE1ODYgODMuMTE5OSA5Ni42MDg2IDgyLjQ2NjcgOTUuNjExN0w2OC43MTY3IDc0Ljk4NjdDNjguMDk4IDc0LjA5MyA2Ny45NjA1IDcyLjk1ODYgNjguMzM4NiA3MS45Mjc0QzY4LjcxNjcgNzAuODk2MSA2OS41NDE3IDcwLjEwNTUgNzAuNTczIDY5Ljc5NjFMMTA4LjQ2OSA1OC40MzMxQzEwOS44MSA1OC4wMjA2IDEwNy43MzIgNTkuNTUyNCAxMDguNjI2IDYwLjYxOEwxMjIuMzc2IDc3LjU5OTJDMTIzLjA2NCA3OC40MjQyIDEyMy4zMDQgNzkuNTU4NiAxMjMuMDI5IDgwLjYyNDJaIiBmaWxsPSIjRkZEQzgzIi8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfOTc5Xzc5MiIgeDE9IjI0IiB5MT0iMy41IiB4Mj0iMTgxIiB5Mj0iMTQ3IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiM0ODAwMDAiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjQUUyMzAwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhcl85NzlfNzkyIiB4MT0iNzQuNSIgeTE9IjE3LjIzNjkiIHgyPSI3NC41IiB5Mj0iNzkuOTEzMyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkZEMjAwIi8+CjxzdG9wIG9mZnNldD0iMC4wNiIgc3RvcC1jb2xvcj0iI0ZGQjUwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuMTQiIHN0b3AtY29sb3I9IiNGRjhDMDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjIxIiBzdG9wLWNvbG9yPSIjRkY3MzAwIi8+CjxzdG9wIG9mZnNldD0iMC4yNiIgc3RvcC1jb2xvcj0iI0ZGNkEwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuMzMiIHN0b3AtY29sb3I9IiNGQzRGMEUiLz4KPHN0b3Agb2Zmc2V0PSIwLjQzIiBzdG9wLWNvbG9yPSIjRjkyRjFFIi8+CjxzdG9wIG9mZnNldD0iMC41MSIgc3RvcC1jb2xvcj0iI0Y4MUIyNyIvPgo8c3RvcCBvZmZzZXQ9IjAuNTciIHN0b3AtY29sb3I9IiNGNzE0MkIiLz4KPHN0b3Agb2Zmc2V0PSIwLjY4IiBzdG9wLWNvbG9yPSIjREYxNjJFIi8+CjxzdG9wIG9mZnNldD0iMC43OSIgc3RvcC1jb2xvcj0iI0FGMUEzOCIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM0QjIxNEMiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDJfbGluZWFyXzk3OV83OTIiIHgxPSI0OC40OTMiIHkxPSIxNS44OTI4IiB4Mj0iNDguNDkzIiB5Mj0iMTAwLjk1NCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkZEMjAwIi8+CjxzdG9wIG9mZnNldD0iMC4wNiIgc3RvcC1jb2xvcj0iI0ZGQjUwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuMTQiIHN0b3AtY29sb3I9IiNGRjhDMDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjIxIiBzdG9wLWNvbG9yPSIjRkY3MzAwIi8+CjxzdG9wIG9mZnNldD0iMC4yNiIgc3RvcC1jb2xvcj0iI0ZGNkEwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuMzMiIHN0b3AtY29sb3I9IiNGQzRGMEUiLz4KPHN0b3Agb2Zmc2V0PSIwLjQzIiBzdG9wLWNvbG9yPSIjRjkyRjFFIi8+CjxzdG9wIG9mZnNldD0iMC41MSIgc3RvcC1jb2xvcj0iI0Y4MUIyNyIvPgo8c3RvcCBvZmZzZXQ9IjAuNTciIHN0b3AtY29sb3I9IiNGNzE0MkIiLz4KPHN0b3Agb2Zmc2V0PSIwLjY4IiBzdG9wLWNvbG9yPSIjREYxNjJFIi8+CjxzdG9wIG9mZnNldD0iMC43OSIgc3RvcC1jb2xvcj0iI0FGMUEzOCIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM0QjIxNEMiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDNfbGluZWFyXzk3OV83OTIiIHgxPSIxMDAuMjkiIHkxPSIxNS44OTI2IiB4Mj0iMTAwLjI5IiB5Mj0iMTAwLjk1MyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkZEMjAwIi8+CjxzdG9wIG9mZnNldD0iMC4wNiIgc3RvcC1jb2xvcj0iI0ZGQjUwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuMTQiIHN0b3AtY29sb3I9IiNGRjhDMDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjIxIiBzdG9wLWNvbG9yPSIjRkY3MzAwIi8+CjxzdG9wIG9mZnNldD0iMC4yNiIgc3RvcC1jb2xvcj0iI0ZGNkEwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuMzMiIHN0b3AtY29sb3I9IiNGQzRGMEUiLz4KPHN0b3Agb2Zmc2V0PSIwLjQzIiBzdG9wLWNvbG9yPSIjRjkyRjFFIi8+CjxzdG9wIG9mZnNldD0iMC41MSIgc3RvcC1jb2xvcj0iI0Y4MUIyNyIvPgo8c3RvcCBvZmZzZXQ9IjAuNTciIHN0b3AtY29sb3I9IiNGNzE0MkIiLz4KPHN0b3Agb2Zmc2V0PSIwLjY4IiBzdG9wLWNvbG9yPSIjREYxNjJFIi8+CjxzdG9wIG9mZnNldD0iMC43OSIgc3RvcC1jb2xvcj0iI0FGMUEzOCIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM0QjIxNEMiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDRfbGluZWFyXzk3OV83OTIiIHgxPSI4OC45MTIyIiB5MT0iMTUuODkyOSIgeDI9Ijg4LjkxMjIiIHkyPSIxMDAuOTU0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRkQyMDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjA2IiBzdG9wLWNvbG9yPSIjRkZCNTAwIi8+CjxzdG9wIG9mZnNldD0iMC4xNCIgc3RvcC1jb2xvcj0iI0ZGOEMwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuMjEiIHN0b3AtY29sb3I9IiNGRjczMDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjI2IiBzdG9wLWNvbG9yPSIjRkY2QTAwIi8+CjxzdG9wIG9mZnNldD0iMC4zMyIgc3RvcC1jb2xvcj0iI0ZDNEYwRSIvPgo8c3RvcCBvZmZzZXQ9IjAuNDMiIHN0b3AtY29sb3I9IiNGOTJGMUUiLz4KPHN0b3Agb2Zmc2V0PSIwLjUxIiBzdG9wLWNvbG9yPSIjRjgxQjI3Ii8+CjxzdG9wIG9mZnNldD0iMC41NyIgc3RvcC1jb2xvcj0iI0Y3MTQyQiIvPgo8c3RvcCBvZmZzZXQ9IjAuNjgiIHN0b3AtY29sb3I9IiNERjE2MkUiLz4KPHN0b3Agb2Zmc2V0PSIwLjc5IiBzdG9wLWNvbG9yPSIjQUYxQTM4Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzRCMjE0QyIvPgo8L2xpbmVhckdyYWRpZW50Pgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50NV9saW5lYXJfOTc5Xzc5MiIgeDE9IjU5Ljg1NyIgeTE9IjE1Ljg5MzgiIHgyPSI1OS44NTciIHkyPSIxMDAuOTU1IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRkQyMDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjA2IiBzdG9wLWNvbG9yPSIjRkZCNTAwIi8+CjxzdG9wIG9mZnNldD0iMC4xNCIgc3RvcC1jb2xvcj0iI0ZGOEMwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuMjEiIHN0b3AtY29sb3I9IiNGRjczMDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjI2IiBzdG9wLWNvbG9yPSIjRkY2QTAwIi8+CjxzdG9wIG9mZnNldD0iMC4zMyIgc3RvcC1jb2xvcj0iI0ZDNEYwRSIvPgo8c3RvcCBvZmZzZXQ9IjAuNDMiIHN0b3AtY29sb3I9IiNGOTJGMUUiLz4KPHN0b3Agb2Zmc2V0PSIwLjUxIiBzdG9wLWNvbG9yPSIjRjgxQjI3Ii8+CjxzdG9wIG9mZnNldD0iMC41NyIgc3RvcC1jb2xvcj0iI0Y3MTQyQiIvPgo8c3RvcCBvZmZzZXQ9IjAuNjgiIHN0b3AtY29sb3I9IiNERjE2MkUiLz4KPHN0b3Agb2Zmc2V0PSIwLjc5IiBzdG9wLWNvbG9yPSIjQUYxQTM4Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzRCMjE0QyIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "whitelistHTTP"], ["spec", "whitelist"], ["spec", "machines"]] + secrets: + exclude: [] + include: [] + services: + exclude: [] + include: + - resourceNames: + - bootbox + ingresses: + exclude: [] + include: + - resourceNames: + - bootbox diff --git a/packages/system/bootbox-rd/templates/cozyrd.yaml b/packages/system/bootbox-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/bootbox-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/bootbox-rd/values.yaml b/packages/system/bootbox-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/bootbox-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/bootbox/templates/bootbox.yaml b/packages/system/bootbox/templates/bootbox.yaml index 496e42e5..a29b4461 100644 --- a/packages/system/bootbox/templates/bootbox.yaml +++ b/packages/system/bootbox/templates/bootbox.yaml @@ -5,17 +5,15 @@ 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: - chart: - spec: - chart: bootbox - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-bootbox-application-default-bootbox + namespace: cozy-system interval: 1m0s timeout: 5m0s diff --git a/packages/system/bucket-rd/Chart.yaml b/packages/system/bucket-rd/Chart.yaml new file mode 100644 index 00000000..352a949c --- /dev/null +++ b/packages/system/bucket-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: bucket-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/bucket-rd/Makefile b/packages/system/bucket-rd/Makefile new file mode 100644 index 00000000..53e89367 --- /dev/null +++ b/packages/system/bucket-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=bucket-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml new file mode 100644 index 00000000..889a36b5 --- /dev/null +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -0,0 +1,42 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: bucket +spec: + application: + kind: Bucket + plural: buckets + singular: bucket + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{}} + release: + prefix: bucket- + labels: + cozystack.io/ui: "true" + chart: + name: bucket + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + singular: Bucket + plural: Buckets + category: IaaS + weight: 80 + description: S3 compatible storage + tags: + - storage + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMzA5MSkiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03MiAzMC4xNjQxTDExNy45ODMgMzYuNzc4OVY0MC42NzM5QzExNy45ODMgNDYuNDY1MyA5Ny4zODYyIDUxLjEzMzIgNzEuOTgyNyA1MS4xMzMyQzQ2LjU3OTIgNTEuMTMzMiAyNiA0Ni40NjUzIDI2IDQwLjY3MzlWMzYuNDQzMUw3MiAzMC4xNjQxWk03MiA1OC4yNjc4QzkxLjIwODQgNTguMjY3OCAxMDcuNjU4IDU1LjU5ODYgMTE0LjU0NyA1MS44MDQ4TDExNi44MDMgNDguMTExTDExNy43MjMgNDQuNzUzVjQ4LjkxNzFMMTAyLjY3OSAxMTEuMDMzQzEwMi42NzkgMTE0Ljg5NSA4OC45NTMzIDExOCA3Mi4wMTcyIDExOEM1NS4wODEyIDExOCA0MS4zNzQzIDExNC44OTUgNDEuMzc0MyAxMTEuMDMzTDI2LjMzIDQ4LjkxNzFWNDQuODM2OUwyOS44MDA3IDUxLjkzODJDMzYuNzA2NSA1NS42NjUzIDUyLjk5OTcgNTguMjY3OCA3MiA1OC4yNjc4WiIgZmlsbD0iIzhDMzEyMyIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcyLjAwMDMgMjZDOTcuNDAzOCAyNiAxMTggMzAuNjgzOSAxMTggMzYuNDQyQzExOCA0Mi4yIDk3LjM4NjYgNDYuODUwNyA3Mi4wMDAzIDQ2Ljg1MDdDNDYuNjE0MSA0Ni44NTA3IDI2LjAxNzYgNDIuMjM0NSAyNi4wMTc2IDM2LjQ0MkMyNi4wMTc2IDMwLjY0OTQgNDYuNTk2OCAyNiA3Mi4wMDAzIDI2Wk03Mi4wMDAzIDU0LjEwMzdDOTUuNjg1NyA1NC4xMDM3IDExNS4xNzIgNTAuMDU4IDExNy43MDYgNDQuODE5N0wxMDIuNjYyIDEwNi45MzdDMTAyLjY2MiAxMTAuNzk5IDg4LjkzNjQgMTEzLjkwNSA3Mi4wMDAzIDExMy45MDVDNTUuMDY0MyAxMTMuOTA1IDQxLjMzOSAxMTAuODE2IDQxLjMzOSAxMDYuOTU0TDI2LjI5NTkgNDQuODM3QzI4Ljg0NjYgNTAuMDU4IDQ4LjMzMzMgNTQuMTAzNyA3Mi4wMDAzIDU0LjEwMzdaIiBmaWxsPSIjRTA1MjQzIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjEuMTcyNSA2MC4wMjkzSDgxLjA5MjhWNzkuMTY3Nkg2MS4xNzI1VjYwLjAyOTNaTTQ1LjMzMDEgOTUuMzY4OEM0NS4zMzAxIDkwLjE0MiA0OS43MTA0IDg1LjkzNDIgNTUuMTUxMSA4NS45MzQyQzYwLjU5MTcgODUuOTM0MiA2NC45NzIxIDkwLjE0MiA2NC45NzIxIDk1LjM2ODhDNjQuOTcyMSAxMDAuNTk2IDYwLjU5MTcgMTA0LjgwMyA1NS4xNTExIDEwNC44MDNDNDkuNzEwNCAxMDQuODAzIDQ1LjMzMDEgMTAwLjU5NiA0NS4zMzAxIDk1LjM2ODhaTTk2LjQ0ODcgMTA0LjM2OEg3Ni43NzIyTDg2LjYxMDUgODYuNzczN0w5Ni40NDg3IDEwNC4zNjhaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4M18zMDkxIiB4MT0iMCIgeTE9IjAiIHgyPSIxNTEiIHkyPSIxODAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRjBFRSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNFQzg4N0QiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"]] + secrets: + exclude: [] + include: + - resourceNames: + - bucket-{{ .name }} + - bucket-{{ .name }}-credentials + ingresses: + exclude: [] + include: + - resourceNames: + - bucket-{{ .name }}-ui diff --git a/packages/system/bucket-rd/templates/cozyrd.yaml b/packages/system/bucket-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/bucket-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/bucket-rd/values.yaml b/packages/system/bucket-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/bucket-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/clickhouse-rd/Chart.yaml b/packages/system/clickhouse-rd/Chart.yaml new file mode 100644 index 00000000..583d39a6 --- /dev/null +++ b/packages/system/clickhouse-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: clickhouse-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/clickhouse-rd/Makefile b/packages/system/clickhouse-rd/Makefile new file mode 100644 index 00000000..4ac0738a --- /dev/null +++ b/packages/system/clickhouse-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=clickhouse-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml b/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml new file mode 100644 index 00000000..6948fce0 --- /dev/null +++ b/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml @@ -0,0 +1,39 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: clickhouse +spec: + application: + kind: ClickHouse + singular: clickhouse + plural: clickhouses + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["cleanupStrategy","enabled","resticPassword","s3AccessKey","s3Bucket","s3Region","s3SecretKey","schedule"],"properties":{"cleanupStrategy":{"description":"Retention strategy for cleaning up old backups.","type":"string","default":"--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"},"enabled":{"description":"Enable regular backups (default: false).","type":"boolean","default":false},"resticPassword":{"description":"Password for Restic backup encryption.","type":"string","default":""},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3Bucket":{"description":"S3 bucket used for storing backups.","type":"string","default":"s3.example.org/clickhouse-backups"},"s3Region":{"description":"AWS S3 region where backups are stored.","type":"string","default":"us-east-1"},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * *"}}},"clickhouseKeeper":{"description":"ClickHouse Keeper configuration.","type":"object","default":{},"properties":{"enabled":{"description":"Deploy ClickHouse Keeper for cluster coordination.","type":"boolean","default":true},"replicas":{"description":"Number of Keeper replicas.","type":"integer","default":3},"resourcesPreset":{"description":"Default sizing preset.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"logStorageSize":{"description":"Size of Persistent Volume for logs.","default":"2Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"logTTL":{"description":"TTL (expiration time) for `query_log` and `query_thread_log`.","type":"integer","default":15},"replicas":{"description":"Number of ClickHouse replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"shards":{"description":"Number of ClickHouse shards.","type":"integer","default":1},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user.","type":"string"},"readonly":{"description":"User is readonly (default: false).","type":"boolean"}}}}}} + release: + prefix: clickhouse- + labels: + cozystack.io/ui: "true" + chart: + name: clickhouse + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: ClickHouse + plural: ClickHouse + description: Managed ClickHouse service + tags: [] + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMzIwMikiLz4KPHBhdGggZD0iTTIzIDEwNUgzNFYxMTZIMjNWMTA1WiIgZmlsbD0iI0ZGMDAwMCIvPgo8cGF0aCBkPSJNMjMgMjhIMzRWMTA1SDIzVjI4Wk00NSAyOEg1NS45OTk5VjExNkg0NVYyOFpNNjYuOTk5OSAyOEg3Ny45OTk5VjExNkg2Ni45OTk5VjI4Wk04OC45OTk5IDI4SDk5Ljk5OTlWMTE2SDg4Ljk5OTlWMjhaTTExMSA2My43NDk5SDEyMlY4MC4yNDk5SDExMVY2My43NDk5WiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODNfMzIwMiIgeDE9Ii0wLjQ5OTk5OCIgeTE9IjEuNSIgeDI9IjE1My41IiB5Mj0iMTYyIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRkNDMDAiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkY3QTAwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "shards"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "logStorageSize"], ["spec", "logTTL"], ["spec", "users"], ["spec", "backup"], ["spec", "backup", "enabled"], ["spec", "backup", "s3Region"], ["spec", "backup", "s3Bucket"], ["spec", "backup", "schedule"], ["spec", "backup", "cleanupStrategy"], ["spec", "backup", "s3AccessKey"], ["spec", "backup", "s3SecretKey"], ["spec", "backup", "resticPassword"], ["spec", "clickhouseKeeper"], ["spec", "clickhouseKeeper", "enabled"], ["spec", "clickhouseKeeper", "size"], ["spec", "clickhouseKeeper", "resourcesPreset"], ["spec", "clickhouseKeeper", "replicas"]] + secrets: + exclude: [] + include: + - resourceNames: + - clickhouse-{{ .name }}-credentials + services: + exclude: [] + include: + - resourceNames: + - chendpoint-clickhouse-{{ .name }} diff --git a/packages/system/clickhouse-rd/templates/cozyrd.yaml b/packages/system/clickhouse-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/clickhouse-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/clickhouse-rd/values.yaml b/packages/system/clickhouse-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/clickhouse-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/cozystack-basics/Chart.yaml b/packages/system/cozystack-basics/Chart.yaml new file mode 100644 index 00000000..8135baef --- /dev/null +++ b/packages/system/cozystack-basics/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: cozy-basics +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/cozystack-basics/Makefile b/packages/system/cozystack-basics/Makefile new file mode 100644 index 00000000..fe85b39b --- /dev/null +++ b/packages/system/cozystack-basics/Makefile @@ -0,0 +1,4 @@ +export NAME=tenant-root +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/cozystack-basics/templates/cozy-public.yaml b/packages/system/cozystack-basics/templates/cozy-public.yaml new file mode 100644 index 00000000..b76816b4 --- /dev/null +++ b/packages/system/cozystack-basics/templates/cozy-public.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: cozy-public diff --git a/packages/system/cozystack-basics/templates/tenant-root.yaml b/packages/system/cozystack-basics/templates/tenant-root.yaml new file mode 100644 index 00000000..42ca6199 --- /dev/null +++ b/packages/system/cozystack-basics/templates/tenant-root.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: tenant-root +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + annotations: + helm.sh/resource-policy: keep + 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 + name: tenant-root + namespace: tenant-root +spec: + chartRef: + kind: ExternalArtifact + name: cozystack-tenant-application-default-tenant + namespace: cozy-system + interval: 1m0s + timeout: 5m0s diff --git a/packages/system/cozystack-basics/values.yaml b/packages/system/cozystack-basics/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/cozystack-basics/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml b/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml index 685d4ac5..c62a5ce3 100644 --- a/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml +++ b/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml @@ -135,29 +135,22 @@ spec: 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. items: - description: |- - CozystackResourceDefinitionResourceSelector 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) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .namespace }}: The namespace of the resource being processed - - Example YAML: - secrets: - include: - - matchExpressions: - - key: badlabel - operator: DoesNotExist - matchLabels: - goodlabel: goodvalue - resourceNames: - - "{{ .name }}-secret" - - "{{ .kind }}-{{ .name }}-tls" - - "specificname" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -217,29 +210,22 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: |- - CozystackResourceDefinitionResourceSelector 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) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .namespace }}: The namespace of the resource being processed - - Example YAML: - secrets: - include: - - matchExpressions: - - key: badlabel - operator: DoesNotExist - matchLabels: - goodlabel: goodvalue - resourceNames: - - "{{ .name }}-secret" - - "{{ .kind }}-{{ .name }}-tls" - - "specificname" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -334,7 +320,6 @@ spec: description: Prefix for the release name type: string required: - - chart - prefix type: object secrets: @@ -346,29 +331,22 @@ spec: 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. items: - description: |- - CozystackResourceDefinitionResourceSelector 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) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .namespace }}: The namespace of the resource being processed - - Example YAML: - secrets: - include: - - matchExpressions: - - key: badlabel - operator: DoesNotExist - matchLabels: - goodlabel: goodvalue - resourceNames: - - "{{ .name }}-secret" - - "{{ .kind }}-{{ .name }}-tls" - - "specificname" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -428,29 +406,22 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: |- - CozystackResourceDefinitionResourceSelector 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) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .namespace }}: The namespace of the resource being processed - - Example YAML: - secrets: - include: - - matchExpressions: - - key: badlabel - operator: DoesNotExist - matchLabels: - goodlabel: goodvalue - resourceNames: - - "{{ .name }}-secret" - - "{{ .kind }}-{{ .name }}-tls" - - "specificname" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -513,29 +484,22 @@ spec: 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. items: - description: |- - CozystackResourceDefinitionResourceSelector 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) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .namespace }}: The namespace of the resource being processed - - Example YAML: - secrets: - include: - - matchExpressions: - - key: badlabel - operator: DoesNotExist - matchLabels: - goodlabel: goodvalue - resourceNames: - - "{{ .name }}-secret" - - "{{ .kind }}-{{ .name }}-tls" - - "specificname" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -595,29 +559,22 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: |- - CozystackResourceDefinitionResourceSelector 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) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .namespace }}: The namespace of the resource being processed - - Example YAML: - secrets: - include: - - matchExpressions: - - key: badlabel - operator: DoesNotExist - matchLabels: - goodlabel: goodvalue - resourceNames: - - "{{ .name }}-secret" - - "{{ .kind }}-{{ .name }}-tls" - - "specificname" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector diff --git a/packages/system/etcd-rd/Chart.yaml b/packages/system/etcd-rd/Chart.yaml new file mode 100644 index 00000000..8aa00f9b --- /dev/null +++ b/packages/system/etcd-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: etcd-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/etcd-rd/Makefile b/packages/system/etcd-rd/Makefile new file mode 100644 index 00000000..ad91bad9 --- /dev/null +++ b/packages/system/etcd-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=etcd-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/etcd-rd/cozyrds/etcd.yaml b/packages/system/etcd-rd/cozyrds/etcd.yaml new file mode 100644 index 00000000..7381469d --- /dev/null +++ b/packages/system/etcd-rd/cozyrds/etcd.yaml @@ -0,0 +1,39 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: etcd +spec: + application: + kind: Etcd + plural: etcds + singular: etcd + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"replicas":{"description":"Number of etcd replicas.","type":"integer","default":3},"resources":{"description":"Resource configuration for etcd.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","default":"1000m","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","default":"512Mi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"size":{"description":"Persistent Volume size.","default":"4Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""}}} + release: + prefix: "" + labels: + cozystack.io/ui: "true" + internal.cozystack.io/tenantmodule: "true" + chart: + name: etcd + sourceRef: + kind: HelmRepository + name: cozystack-extra + namespace: cozy-public + dashboard: + category: Administration + singular: Etcd + plural: Etcd + name: etcd + description: Storage for Kubernetes clusters + module: true + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9Ii0wLjAwMTk1MzEyIiB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgcng9IjI0IiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfNjgzXzI5NjMpIi8+CjxwYXRoIGQ9Ik0xMjIuNDQyIDczLjQ3MjlDMTIxLjk1OSA3My41MTM0IDEyMS40NzQgNzMuNTMyMiAxMjAuOTU4IDczLjUzMjJDMTE3Ljk2NSA3My41MzIyIDExNS4wNjEgNzIuODMwNCAxMTIuNDQyIDcxLjU0NTFDMTEzLjMxNCA2Ni41NDIxIDExMy42ODUgNjEuNTAxOSAxMTMuNTg4IDU2LjQ4MDJDMTEwLjc0OCA1Mi4zNzIzIDEwNy41MDIgNDguNDk2NSAxMDMuODM4IDQ0LjkyNTdDMTA1LjQyOCA0MS45NDU0IDEwNy43NzggMzkuMzgxMSAxMTAuNzExIDM3LjU2MjhMMTExLjk3MSAzNi43ODQyTDExMC45ODkgMzUuNjc3NEMxMDUuOTMyIDI5Ljk4MzIgOTkuODk3MSAyNS41ODA5IDkzLjA1NDcgMjIuNTkzN0w5MS42OTAyIDIyTDkxLjM0MzcgMjMuNDQyM0M5MC41Mjc3IDI2LjgwMzYgODguODIyMiAyOS44MzYgODYuNDgwNyAzMi4yNjk1QzgxLjk4MDMgMjkuODc3NCA3Ny4yNzg4IDI3Ljk0NCA3Mi40MzA1IDI2LjQ3OTdDNjcuNTkzNyAyNy45NDA4IDYyLjkwMDUgMjkuODY4NiA1OC40MDIgMzIuMjU3MUM1Ni4wNzAxIDI5LjgyNjggNTQuMzY4OCAyNi44MDE4IDUzLjU1NiAyMy40NTAxTDUzLjIwNzIgMjIuMDA4M0w1MS44NDc3IDIyLjU5OTJDNDUuMDkxNCAyNS41NDMxIDM4Ljg5MDEgMzAuMDY0NyAzMy45MTYyIDM1LjY3NDJMMzIuOTMxOCAzNi43ODMzTDM0LjE5IDM3LjU2MTlDMzcuMTE0MiAzOS4zNzMzIDM5LjQ1NzYgNDEuOTIyNCA0MS4wNDQ0IDQ0Ljg4NjZDMzcuMzkxNyA0OC40NDM1IDM0LjE0OTUgNTIuMzA3IDMxLjMxMTkgNTYuMzk1OUMzMS4yMDE0IDYxLjQxNTQgMzEuNTUzNSA2Ni40OTI0IDMyLjQyOTcgNzEuNTY0NEMyOS44MjMxIDcyLjgzNzggMjYuOTM1OCA3My41MzE4IDIzLjk2MjggNzMuNTMxOEMyMy40NDA5IDczLjUzMTggMjIuOTUyNyA3My41MTI5IDIyLjQ3ODIgNzMuNDczM0wyMSA3My4zNjA2TDIxLjEzODUgNzQuODM2NUMyMS44NjI5IDgyLjMwMzMgMjQuMTgxNCA4OS40MDUzIDI4LjAzMzQgOTUuOTQ3MUwyOC43ODUzIDk3LjIyMzdMMjkuOTE0MiA5Ni4yNjU2QzMyLjUzMDUgOTQuMDQ2NSAzNS42OTE3IDkyLjU3NyAzOS4wNTMgOTEuOTg0N0M0MS4yNjg5IDk2LjUxNTUgNDMuODk1MyAxMDAuNzcyIDQ2Ljg3NDcgMTA0LjcyNUM1MS42Mjg3IDEwNi4zODcgNTYuNTgxOSAxMDcuNjI5IDYxLjY5NzEgMTA4LjM2N0M2Mi4xODc3IDExMS43NSA2MS43OTcgMTE1LjI0OSA2MC40NjI0IDExOC40ODRMNTkuODk5NSAxMTkuODU1TDYxLjM0NjkgMTIwLjE3NEM2NS4wNTI5IDEyMC45ODkgNjguNzkxNyAxMjEuNDA0IDcyLjQ1MjYgMTIxLjQwNEw4My41NTUxIDEyMC4xNzRMODUuMDAzOSAxMTkuODU1TDg0LjQzOTcgMTE4LjQ4MkM4My4xMDg3IDExNS4yNDYgODIuNzE4IDExMS43NDMgODMuMjA4NiAxMDguMzZDODguMzAzNiAxMDcuNjIxIDkzLjIzODQgMTA2LjM4MiA5Ny45NzQ4IDEwNC43MjVDMTAwLjk1NyAxMDAuNzY5IDEwMy41ODYgOTYuNTA5NSAxMDUuODA1IDkxLjk3MjhDMTA5LjE3NyA5Mi41NjE0IDExMi4zNTYgOTQuMDMxNyAxMTQuOTg5IDk2LjI1NzNMMTE2LjExOCA5Ny4yMTQxTDExNi44NjYgOTUuOTQwN0MxMjAuNzI1IDg5LjM5MDUgMTIzLjA0MyA4Mi4yODkxIDEyMy43NTYgNzQuODM0MkwxMjMuODk1IDczLjM2MUwxMjIuNDQyIDczLjQ3MjlaTTg4LjMxOTcgOTEuNTE4MUM4My4wNjczIDkyLjk0NjYgNzcuNzMzIDkzLjY2NzcgNzIuNDMwNSA5My42Njc3QzY3LjExMzcgOTMuNjY3NyA2MS43ODU5IDkyLjk0NyA1Ni41MjkgOTEuNTE4MUM1My42NDQ4IDg3LjAzNjYgNTEuMzY0NSA4Mi4yMzU3IDQ5LjcyMzQgNzcuMTgxMkM0OC4wODkyIDcyLjE1MDIgNDcuMTMyOSA2Ni44Nzk1IDQ2Ljg1NTQgNjEuNDUyMkM1MC4yNTA0IDU3LjI1NDcgNTQuMTExIDUzLjU3NzYgNTguMzc2NyA1MC40ODIzQzYyLjcxMTQgNDcuMzI5NCA2Ny40MjcxIDQ0Ljc2NzkgNzIuNDMwNSA0Mi44NDFDNzcuNDI1NiA0NC43NjgzIDgyLjEzMjYgNDcuMzI2MiA4Ni40NTcyIDUwLjQ2NTdDOTAuNzM5NCA1My41Nzc2IDk0LjYxNzEgNTcuMjgzMiA5OC4wMjg3IDYxLjUwN0M5Ny43Mzc4IDY2LjkwMzQgOTYuNzcgNzIuMTQzOCA5NS4xMzMgNzcuMTY2NUM5My40OTYxIDgyLjIyIDkxLjIwODQgODcuMDM2MSA4OC4zMTk3IDkxLjUxODFaTTc2Ljc2ODQgNjYuMTk3NEM3Ni43Njg0IDY5LjkwODEgNzkuNzc1NCA3Mi45MDk2IDgzLjQ4MSA3Mi45MDk2Qzg3LjE4NTcgNzIuOTA5NiA5MC4xODk1IDY5LjkwODYgOTAuMTg5NSA2Ni4xOTc0QzkwLjE4OTUgNjIuNTAxIDg3LjE4NTcgNTkuNDg4MSA4My40ODEgNTkuNDg4MUM3OS43NzU0IDU5LjQ4ODEgNzYuNzY4NCA2Mi41MDEgNzYuNzY4NCA2Ni4xOTc0Wk02OC4wOTU0IDY2LjE5NzRDNjguMDk1NCA2OS45MDgxIDY1LjA4ODggNzIuOTA5NiA2MS4zODMyIDcyLjkwOTZDNTcuNjc0OSA3Mi45MDk2IDU0LjY3NjYgNjkuOTA4NiA1NC42NzY2IDY2LjE5NzRDNTQuNjc2NiA2Mi41MDI0IDU3LjY3NTMgNTkuNDg5NCA2MS4zODMyIDU5LjQ4OTRDNjUuMDg4OCA1OS40ODk0IDY4LjA5NTQgNjIuNTAyNCA2OC4wOTU0IDY2LjE5NzRaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4M18yOTYzIiB4MT0iNS41IiB5MT0iMTEiIHgyPSIxNDEiIHkyPSIxMjQuNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjNTNCMkYwIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzQxOUVEQSIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "size"], ["spec", "storageClass"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resources", "cpu"], ["spec", "resources", "memory"]] + secrets: + exclude: [] + include: [] + services: + exclude: [] + include: + - resourceNames: + - etcd diff --git a/packages/system/etcd-rd/templates/cozyrd.yaml b/packages/system/etcd-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/etcd-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/etcd-rd/values.yaml b/packages/system/etcd-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/etcd-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/ferretdb-rd/Chart.yaml b/packages/system/ferretdb-rd/Chart.yaml new file mode 100644 index 00000000..6e2730f1 --- /dev/null +++ b/packages/system/ferretdb-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: ferretdb-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/ferretdb-rd/Makefile b/packages/system/ferretdb-rd/Makefile new file mode 100644 index 00000000..f6814169 --- /dev/null +++ b/packages/system/ferretdb-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=ferretdb-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml b/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml new file mode 100644 index 00000000..f7bdeb82 --- /dev/null +++ b/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml @@ -0,0 +1,40 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: ferretdb +spec: + application: + kind: FerretDB + plural: ferretdbs + singular: ferretdb + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["destinationPath","enabled","endpointURL","retentionPolicy","s3AccessKey","s3SecretKey","schedule"],"properties":{"destinationPath":{"description":"Path to store the backup (e.g. s3://bucket/path/to/folder/).","type":"string","default":"s3://bucket/path/to/folder/"},"enabled":{"description":"Enable regular backups (default: false).","type":"boolean","default":false},"endpointURL":{"description":"S3 endpoint URL for uploads.","type":"string","default":"http://minio-gateway-service:9000"},"retentionPolicy":{"description":"Retention policy.","type":"string","default":"30d"},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * * *"}}},"bootstrap":{"description":"Bootstrap configuration.","type":"object","default":{},"properties":{"enabled":{"description":"Restore database cluster from a backup.","type":"boolean","default":false},"oldName":{"description":"Name of database cluster before deletion.","type":"string","default":""},"recoveryTime":{"description":"Timestamp (RFC3339) for point-in-time recovery; empty means latest.","type":"string","default":""}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"quorum":{"description":"Configuration for quorum-based synchronous replication.","type":"object","default":{},"required":["maxSyncReplicas","minSyncReplicas"],"properties":{"maxSyncReplicas":{"description":"Maximum number of synchronous replicas allowed (must be less than total replicas).","type":"integer","default":0},"minSyncReplicas":{"description":"Minimum number of synchronous replicas required for commit.","type":"integer","default":0}}},"replicas":{"description":"Number of replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each FerretDB replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user.","type":"string"}}}}}} + release: + prefix: ferretdb- + labels: + cozystack.io/ui: "true" + chart: + name: ferretdb + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: FerretDB + plural: FerretDB + description: Managed FerretDB service + tags: + - database + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9Ii0wLjAwMTk1MzEyIiB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgcng9IjI0IiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfNjgzXzI5NTIpIi8+CjxwYXRoIGQ9Ik02OS41OTIzIDIyLjEzMUM1OC4yNjYyIDIzLjY3ODcgNDYuOTAzNyAzMC44NzE0IDQwLjMzMDIgNDAuNjY3OUMzOS4yNzQgNDIuMjUyMSAzNy40NTMxIDQ1LjU0OCAzNy40NTMxIDQ1Ljg3NTdDMzcuNDUzMSA0NS45MTIyIDM4LjMyNzIgNDUuMzg0MSAzOS4zODMzIDQ0LjY5MjFDNTIuMzg0NyAzNi4xMTU2IDY3Ljg5ODkgMzQuNTMxNCA4MC41MTc4IDQwLjQ4NThDODMuMjY3NCA0MS43Nzg3IDg0Ljk5NzMgNDMuMDM1MSA4Ny40NTU1IDQ1LjQ5MzNDOTEuNTg5IDQ5LjY0NSA5NC42MTE3IDU1LjE5ODggOTYuNzA1OCA2Mi41MDA3Qzk3Ljc5ODMgNjYuMjUxOCA5OC43MDg4IDcxLjM2ODYgOTguOTQ1NSA3NC44NDY1Qzk5LjAwMDEgNzUuNzkzNCA5OS4xNDU4IDc2LjYzMSA5OS4yMzY5IDc2LjY4NTZDOTkuNzQ2NyA3Ni45OTUyIDEwMi4wNDEgNzMuNjYyOSAxMDMuNjYyIDcwLjI3NkMxMDYuMjI5IDY0Ljg4NjEgMTA3LjQzMSA1OS41ODcyIDEwNy40MTMgNTMuNzA1N0MxMDcuMzk1IDQ1LjM4NDEgMTA0LjUxOCAzOC4zOTE3IDk4LjcyNyAzMi41NjQ4QzkzLjU5MiAyNy4zOTM0IDg3LjEwOTUgMjMuODQyNiA4MC4zMTc1IDIyLjQ1ODdDNzguNzMzMyAyMi4xNDkyIDc3LjU2NzkgMjIuMDU4MSA3NC41OTk5IDIyLjAwMzVDNzIuNTQyMiAyMS45ODUzIDcwLjMwMjUgMjIuMDM5OSA2OS41OTIzIDIyLjEzMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik00NS41MiA0Ni40NDAyQzQ0LjMzNjQgNDcuMDIyOSA0Mi4zNTE2IDQ4Ljg0MzggNDAuNjAzNSA1MC45Mzc5QzM5LjgyMDUgNTEuODY2NiAzOC42MzY5IDUzLjAxMzcgMzcuNzYyOSA1My42NjkzQzM1LjcyMzQgNTUuMTk4OSAzMi4yNDU1IDU4LjYwNCAzMC40NzkyIDYwLjgwNzNDMjEuMjY1NCA3Mi4yMjQ0IDE4LjY5NzkgODUuMjQ0IDIzLjA4NjMgOTguMzE4MkMyNi42OTE3IDEwOS4wMjUgMzUuMDMxNSAxMTYuMTI3IDQ3Ljg1MDggMTE5LjM1QzUyLjg0MDEgMTIwLjYyNCA2MC4zMjQgMTIxLjMzNSA2My40NTYgMTIwLjg0M0w2NC4yNTcyIDEyMC43MTVMNjMuMDE5IDExOS45ODdDNTYuMTkwNiAxMTYuMDE4IDUxLjQxOTggMTA5LjMxNyA1MC4wOTA1IDEwMS44NjlDNDkuNjg5OSA5OS42MTEgNDkuNjcxNyA5NS42MDUgNTAuMDcyMyA5My40MDE3QzUwLjk2NDUgODguNDQ4OCA1My40NTkyIDgzLjg5NjUgNTYuODQ2MSA4MS4wNTU5QzU4LjQzMDMgNzkuNzI2NiA2MS4xOTgxIDc4LjM2MDkgNjMuNDAxNCA3Ny44MzI5QzY2LjcxNTUgNzcuMDMxNyA2OC43MzY3IDc2LjEyMTIgNzAuODMwNyA3NC40NjQyQzcyLjE3ODIgNzMuNDA4IDczLjM2MTggNzEuODA1NiA3NC4zNDUxIDY5LjcyOThDNzUuMTgyNyA2Ny45NjM1IDc2Ljk2NzIgNjIuMzU1MSA3Ni45NjcyIDYxLjQ2MjhDNzYuOTY3MiA2MC44NDM3IDc2LjMyOTkgNjAuMDA2MSA3NS40MTk1IDU5LjQ0MTZDNzQuOTQ2IDU5LjE1MDIgNzQuMTk5NCA1OC45ODY0IDcyLjI4NzUgNTguNzg2MUM2NC4wNTY5IDU3LjkzMDIgNTkuOTU5OSA1Ni40MzcxIDU1LjAwNyA1Mi41MjIxQzU0LjI5NjggNTEuOTU3NiA1My40NDEgNTEuMzIwMyA1My4wOTUgNTEuMTAxOEM1Mi43NDkgNTAuOTAxNSA1Mi4wNTcxIDUwLjEzNjcgNTEuNTgzNiA0OS40MjY1QzUwLjE0NTEgNDcuMzMyNSA0OC4zNjA2IDQ1Ljk4NSA0Ni45OTQ5IDQ1Ljk2NjhDNDYuNzAzNiA0NS45NjY4IDQ2LjAyOTggNDYuMTg1MyA0NS41MiA0Ni40NDAyWk01NC40NjA3IDU0Ljg3MTFDNTUuMDc5OCA1NS4xODA2IDU1Ljc1MzUgNTUuNTgxMiA1NS45NzIgNTUuNzQ1MUw1Ni4zNzI3IDU2LjA3MjlMNTUuNzM1MyA1OC42MjIyQzU1LjE4OTEgNjAuODQzNyA1NS4wOTggNjEuNDA4MiA1NS4xNTI2IDYyLjk5MjRDNTUuMjA3MyA2NC41NTg0IDU1LjI2MTkgNjQuOTA0MyA1NS42MjYxIDY1LjQxNDJDNTYuMjI3IDY2LjIzMzYgNTcuMjY0OSA2Ni43MjUzIDU4LjQzMDMgNjYuNzI1M0M2MC4wODczIDY2LjcyNTMgNjEuMzgwMiA2NS43Nzg0IDYzLjUyODkgNjIuOTU2QzY0LjE0OCA2Mi4xNTQ4IDY0LjYzOTYgNjEuNzE3NyA2NS4zNjggNjEuMzcxOEM2Ni40OTcgNjAuODA3MyA2Ny4yOTgyIDYwLjc1MjcgNjkuODExIDYwLjk3MTJMNzEuNDg2MyA2MS4xMzVWNjIuMTE4M0M3MS40ODYzIDYzLjY2NjEgNzIuMzA1NyA2NC41NTg0IDczLjk4MDkgNjQuODEzM0w3NC43ODIxIDY0LjkyMjZMNzQuNDkwOCA2NS41OTYzQzczLjIxNjEgNjguNjczNiA2OS45Mzg1IDcyLjE1MTYgNjYuODYxMSA3My42OTk0QzY2LjM2OTUgNzMuOTM2MSA2NS4yNTg3IDc0LjM3MzEgNjQuNDAyOSA3NC42NjQ1QzYzLjAwMDggNzUuMTE5NyA2Mi42MTg0IDc1LjE3NDMgNjAuMjE0OCA3NS4xNzQzQzU3LjgyOTQgNzUuMTc0MyA1Ny40Mjg4IDc1LjExOTcgNTYuMTE3NyA3NC42ODI3QzUyLjE2NjMgNzMuMzcxNiA0OS4yMzQ3IDcwLjQ1ODEgNDcuOTA1NCA2Ni41NDMyQzQ3LjQzMTkgNjUuMTU5MyA0Ny40MTM3IDYxLjEzNSA0Ny44ODcyIDU5LjQ1OThDNDguNTI0NSA1Ny4xNDcyIDQ5LjY1MzUgNTUuMjM1MyA1MC44MzcxIDU0LjQ4ODdDNTEuNjAxOCA1My45OTcgNTMuMDIyMiA1NC4xNjA5IDU0LjQ2MDcgNTQuODcxMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMTMuMDIyIDYxLjczNjFDMTEzLjAyMiA2Mi41NTU1IDExMi4xMTEgNjYuMzQzMSAxMTEuMzQ3IDY4LjcxMDJDMTA4LjQ3IDc3LjU3ODEgMTAzLjI2MiA4NS41MzU1IDk2LjQ2OTcgOTEuMzQ0M0M5MS42OTg5IDk1LjQ0MTMgODguMzExOSA5Ny4yNDQgODIuOTQwMiA5OC41NzMzQzc5LjQ4MDUgOTkuNDI5MSA3Ny4yMjI2IDk5LjcwMjMgNzIuODM0MSA5OS44MTE1QzY3LjM1MzIgOTkuOTU3MiA2MS45NDUxIDk5LjQ2NTUgNTcuMTAxNCA5OC40MDk0QzU2LjE3MjcgOTguMjA5MSA1NS4zODk4IDk4LjA4MTYgNTUuMzM1MSA5OC4xMzYzQzU1LjExNjYgOTguMzM2NiA1NS45NTQyIDEwMS4xMjMgNTYuNjgyNiAxMDIuNTk4QzU4LjAxMTkgMTA1LjMyOSA1OS41MjMyIDEwNy4zNjggNjIuMjE4MiAxMTAuMDYzQzY1LjA1ODggMTEyLjkwNCA2Ny4xNzExIDExNC40NyA3MC40NDg3IDExNi4xNjNDNzguNTcgMTIwLjM1MSA4Ny44OTMxIDEyMC45MTYgOTcuNDUzIDExNy43NjZDMTA3LjU0MSAxMTQuNDcgMTE0Ljk1MiAxMDguNTE2IDExOC45NCAxMDAuNTAzQzEyMS41OTggOTUuMTg2NCAxMjIuNjkxIDg5LjUwNTEgMTIyLjI5IDgzLjAyMjdDMTIxLjc5OSA3NS4wMjg4IDExOC44NDkgNjcuMTk4OSAxMTQuNTcgNjIuNTczOEMxMTMuODk2IDYxLjg0NTQgMTEzLjI3NyA2MS4yNjI3IDExMy4xODYgNjEuMjYyN0MxMTMuMDk1IDYxLjI2MjcgMTEzLjAyMiA2MS40ODEyIDExMy4wMjIgNjEuNzM2MVoiIGZpbGw9IndoaXRlIi8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfNjgzXzI5NTIiIHgxPSI1LjUiIHkxPSIxMSIgeDI9IjE0MSIgeTI9IjEyNC41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiM0NUFEQzYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMjE2Nzc4Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "quorum"], ["spec", "quorum", "minSyncReplicas"], ["spec", "quorum", "maxSyncReplicas"], ["spec", "users"], ["spec", "backup"], ["spec", "backup", "enabled"], ["spec", "backup", "schedule"], ["spec", "backup", "retentionPolicy"], ["spec", "backup", "endpointURL"], ["spec", "backup", "destinationPath"], ["spec", "backup", "s3AccessKey"], ["spec", "backup", "s3SecretKey"], ["spec", "bootstrap"], ["spec", "bootstrap", "enabled"], ["spec", "bootstrap", "recoveryTime"], ["spec", "bootstrap", "oldName"]] + secrets: + exclude: [] + include: + - resourceNames: + - ferretdb-{{ .name }}-credentials + services: + exclude: [] + include: + - resourceNames: + - ferretdb-{{ .name }} diff --git a/packages/system/ferretdb-rd/templates/cozyrd.yaml b/packages/system/ferretdb-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/ferretdb-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/ferretdb-rd/values.yaml b/packages/system/ferretdb-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/ferretdb-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/foundationdb-rd/Chart.yaml b/packages/system/foundationdb-rd/Chart.yaml new file mode 100644 index 00000000..1c8afe3a --- /dev/null +++ b/packages/system/foundationdb-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: foundationdb-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/foundationdb-rd/Makefile b/packages/system/foundationdb-rd/Makefile new file mode 100644 index 00000000..b6e6d41a --- /dev/null +++ b/packages/system/foundationdb-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=foundationdb-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml b/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml new file mode 100644 index 00000000..e7759380 --- /dev/null +++ b/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml @@ -0,0 +1,30 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: foundationdb +spec: + application: + kind: FoundationDB + singular: foundationdb + plural: foundationdbs + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"automaticReplacements":{"description":"Enable automatic pod replacements","type":"boolean","default":true},"backup":{"description":"Backup configuration","type":"object","default":{"enabled":false,"retentionPolicy":"7d","s3":{"bucket":"","credentials":{"accessKeyId":"","secretAccessKey":""},"endpoint":"","region":"us-east-1"}},"required":["enabled","retentionPolicy","s3"],"properties":{"enabled":{"description":"Enable backups","type":"boolean","default":false},"retentionPolicy":{"description":"Retention policy for backups","type":"string","default":"7d"},"s3":{"description":"S3 configuration for backups","type":"object","default":{"bucket":"","credentials":{"accessKeyId":"","secretAccessKey":""},"endpoint":"","region":"us-east-1"},"required":["bucket","credentials","endpoint","region"],"properties":{"bucket":{"description":"S3 bucket name","type":"string"},"credentials":{"description":"S3 credentials","type":"object","default":{"accessKeyId":"","secretAccessKey":""},"required":["accessKeyId","secretAccessKey"],"properties":{"accessKeyId":{"description":"S3 access key ID","type":"string"},"secretAccessKey":{"description":"S3 secret access key","type":"string"}}},"endpoint":{"description":"S3 endpoint URL","type":"string"},"region":{"description":"S3 region","type":"string","default":"us-east-1"}}}}},"cluster":{"description":"Cluster configuration","type":"object","default":{"faultDomain":{"key":"kubernetes.io/hostname","valueFrom":"spec.nodeName"},"processCounts":{"cluster_controller":1,"stateless":-1,"storage":3},"redundancyMode":"double","storageEngine":"ssd-2","version":"7.3.63"},"required":["faultDomain","processCounts","redundancyMode","storageEngine","version"],"properties":{"faultDomain":{"description":"Fault domain configuration","type":"object","default":{"key":"kubernetes.io/hostname","valueFrom":"spec.nodeName"},"required":["key","valueFrom"],"properties":{"key":{"description":"Fault domain key","type":"string","default":"kubernetes.io/hostname"},"valueFrom":{"description":"Fault domain value source","type":"string","default":"spec.nodeName"}}},"processCounts":{"description":"Process counts for different roles","type":"object","default":{"cluster_controller":1,"stateless":-1,"storage":3},"required":["cluster_controller","stateless","storage"],"properties":{"cluster_controller":{"description":"Number of cluster controller processes","type":"integer","default":1},"stateless":{"description":"Number of stateless processes (-1 for automatic)","type":"integer","default":-1},"storage":{"description":"Number of storage processes (determines cluster size)","type":"integer","default":3}}},"redundancyMode":{"description":"Database redundancy mode (single, double, triple, three_datacenter, three_datacenter_fallback)","type":"string","default":"double"},"storageEngine":{"description":"Storage engine (ssd-2, ssd-redwood-v1, ssd-rocksdb-v1, memory)","type":"string","default":"ssd-2"},"version":{"description":"Version of FoundationDB to use","type":"string","default":"7.3.63"}}},"customParameters":{"description":"Custom parameters to pass to FoundationDB","type":"array","default":[],"items":{"type":"string"}},"imageType":{"description":"Container image deployment type","type":"string","default":"unified","enum":["unified","split"]},"monitoring":{"description":"Monitoring configuration","type":"object","default":{"enabled":true},"required":["enabled"],"properties":{"enabled":{"description":"Enable WorkloadMonitor integration","type":"boolean","default":true}}},"resources":{"description":"Explicit CPU and memory configuration for each FoundationDB instance. When left empty, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each instance","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each instance","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted. Allowed values: `small`, `medium`, `large`, `xlarge`, `2xlarge`.","type":"string","default":"medium","enum":["small","medium","large","xlarge","2xlarge"]},"securityContext":{"description":"Security context for containers","type":"object","default":{"runAsGroup":4059,"runAsUser":4059},"required":["runAsGroup","runAsUser"],"properties":{"runAsGroup":{"description":"Group ID to run the container","type":"integer","default":4059},"runAsUser":{"description":"User ID to run the container","type":"integer","default":4059}}},"storage":{"description":"Storage configuration","type":"object","default":{"size":"16Gi","storageClass":""},"required":["size","storageClass"],"properties":{"size":{"description":"Size of persistent volumes for each instance","default":"16Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"Storage class (if not set, uses cluster default)","type":"string"}}}}} + release: + prefix: foundationdb- + labels: + cozystack.io/ui: "true" + chart: + name: foundationdb + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: FoundationDB + plural: FoundationDB + description: Managed FoundationDB service + tags: + - database + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX3JhZGlhbF84NThfMzA3MikiLz4KPHBhdGggZD0iTTEzNS43ODQgNzUuNjQ0NkwxMzUuOTM5IDg3Ljc2MzhMODkuNjg0NiA4MS41MzYyTDYyLjA4NjggODQuNTA3OUwzNS4zNDE3IDgxLjQzMjlMOC43NTE2NyA4NC41ODU0TDguNzI1ODMgODEuNTEwNEwzNS4zNjc2IDc3LjU4MjZWNjQuMTcxM0w2Mi4yOTM1IDcwLjczNDhMNjIuMzQ1MiA4MS4yNzc4TDg5LjQ3NzkgNzcuNjg2TDg5LjQwMDQgNjQuMTk3MkwxMzUuNzg0IDc1LjY0NDZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNODkuNDc3OCA4Ni4wMzI1TDEzNS44ODggOTAuODM4OFYxMDIuNzI2SDguNjQ4MjVMOC41MTkwNCA5OS41NzNIMzUuMjY0MUMzNS4yNjQxIDk5LjU3MyAzNS4yNjQxIDkwLjczNTUgMzUuMjY0MSA4Ni4wNTgzQzQ0LjI1NjcgODYuOTM2OSA2Mi4wODY3IDg4LjY5NDEgNjIuMDg2NyA4OC42OTQxVjk5LjI2MjlIODkuNDc3OFY4Ni4wMzI1WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTYyLjI5MzQgNjYuODg0Nkw2Mi4yMTU4IDYzLjYyODZDNjIuMjE1OCA2My42Mjg2IDc5LjgxMzMgNTguMzU3MSA4OC45MDkyIDU1LjY2OTdDODguOTA5MiA1MS4zMDI2IDg4LjkwOTIgNDcuMDkwNiA4OC45MDkyIDQyQzEwNC44NzkgNDguNDA4NSAxMjAuMjI4IDU0LjYxMDIgMTM1LjczMyA2MC44Mzc4QzEzNS43MzMgNjQuNzEzOSAxMzUuNzMzIDY4LjQzNSAxMzUuNzMzIDcyLjU2OTVDMTE5Ljg0MSA2OC4yMDI0IDEwNC4yODQgNjMuOTEyOSA4OS4xNjc2IDU5Ljc1MjVDNzkuOTY4NCA2Mi4yMDc0IDYyLjI5MzQgNjYuODg0NiA2Mi4yOTM0IDY2Ljg4NDZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzUuMzk2MiA4MS43MDczTDguODA2MTIgODQuODU5OEw4Ljc4MDI3IDgxLjc4NDhMMzUuNDIyIDc3Ljg1N1Y2NC40NDU3TDYyLjM0OCA3MS4wMDkzTDYyLjM5OTYgODEuNTUyMkw4OS41MzIzIDc3Ljk2MDRMODkuNDU0OCA2NC40NzE2TDEzNS44MzkgNzUuOTE5TDEzNS45OTQgODguMDM4Mkw4OS43MzkxIDgxLjgxMDZMNjIuMTQxMiA4NC43ODIzTDM1LjM5NjIgODEuNzA3M1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik04OS41MzIzIDg2LjMwNjlMMTM1Ljk0MiA5MS4xMTMzVjEwM0g4LjcwMjdMOC41NzM0OSA5OS44NDc0SDM1LjMxODZDMzUuMzE4NiA5OS44NDc0IDM1LjMxODYgOTEuMDA5OSAzNS4zMTg2IDg2LjMzMjhDNDQuMzExMSA4Ny4yMTE0IDYyLjE0MTIgODguOTY4NSA2Mi4xNDEyIDg4Ljk2ODVWOTkuNTM3M0g4OS41MzIzVjg2LjMwNjlaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNjIuMzQ4MyA2Ny4xNTlMNjIuMjcwOCA2My45MDMxQzYyLjI3MDggNjMuOTAzMSA3OS44NjgyIDU4LjYzMTYgODguOTY0MiA1NS45NDQyQzg4Ljk2NDIgNTEuNTc3MSA4OC45NjQyIDQ3LjM2NTEgODguOTY0MiA0Mi4yNzQ0QzEwNC45MzQgNDguNjgyOSAxMjAuMjgzIDU0Ljg4NDcgMTM1Ljc4NyA2MS4xMTIzQzEzNS43ODcgNjQuOTg4NCAxMzUuNzg3IDY4LjcwOTQgMTM1Ljc4NyA3Mi44NDM5QzExOS44OTUgNjguNDc2OSAxMDQuMzM5IDY0LjE4NzMgODkuMjIyNiA2MC4wMjdDODAuMDIzMyA2Mi40ODE4IDYyLjM0ODMgNjcuMTU5IDYyLjM0ODMgNjcuMTU5WiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQwX3JhZGlhbF84NThfMzA3MiIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtMjkuNSAtMTgpIHJvdGF0ZSgzOS42OTYzKSBzY2FsZSgzMDIuMTY4IDI3NS4yNzEpIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0JFRERGRiIvPgo8c3RvcCBvZmZzZXQ9IjAuMjU5NjE1IiBzdG9wLWNvbG9yPSIjOUVDQ0ZEIi8+CjxzdG9wIG9mZnNldD0iMC41OTEzNDYiIHN0b3AtY29sb3I9IiMzRjlBRkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMEI3MEUwIi8+CjwvcmFkaWFsR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== + # keysOrder: [] diff --git a/packages/system/foundationdb-rd/templates/cozyrd.yaml b/packages/system/foundationdb-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/foundationdb-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/foundationdb-rd/values.yaml b/packages/system/foundationdb-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/foundationdb-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/http-cache-rd/Chart.yaml b/packages/system/http-cache-rd/Chart.yaml new file mode 100644 index 00000000..c82ad1aa --- /dev/null +++ b/packages/system/http-cache-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: http-cache-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/http-cache-rd/Makefile b/packages/system/http-cache-rd/Makefile new file mode 100644 index 00000000..cf367cf4 --- /dev/null +++ b/packages/system/http-cache-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=http-cache-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/http-cache-rd/cozyrds/http-cache.yaml b/packages/system/http-cache-rd/cozyrds/http-cache.yaml new file mode 100644 index 00000000..70de0418 --- /dev/null +++ b/packages/system/http-cache-rd/cozyrds/http-cache.yaml @@ -0,0 +1,34 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: http-cache +spec: + application: + kind: HTTPCache + plural: httpcaches + singular: httpcache + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"endpoints":{"description":"Endpoints configuration, as a list of .","type":"array","default":[],"items":{"type":"string"}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"haproxy":{"description":"HAProxy configuration.","type":"object","default":{},"required":["replicas","resourcesPreset"],"properties":{"replicas":{"description":"Number of HAProxy replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"nginx":{"description":"Nginx configuration.","type":"object","default":{},"required":["replicas","resourcesPreset"],"properties":{"replicas":{"description":"Number of Nginx replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""}}} + release: + prefix: http-cache- + labels: + cozystack.io/ui: "true" + chart: + name: http-cache + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: NaaS + singular: HTTP Cache + plural: HTTP Cache + description: Layer7 load balancer and caching service + tags: + - cache + - network + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODFfMjgyNSkiLz4KPHBhdGggZD0iTTI2LjAwMjYgMzcuODU4OEMyNi4wMDI2IDYwLjkxOSAyNi4wMDI2IDgzLjk4MTQgMjYuMDAyNiAxMDcuMDQ2QzI1Ljk3MyAxMDguMzIzIDI2LjE5OTYgMTA5LjU5MyAyNi42NjkyIDExMC43ODNDMjcuMTM4NyAxMTEuOTcyIDI3Ljg0MTggMTEzLjA1NiAyOC43Mzc0IDExMy45NzJDMzAuNDUzOSAxMTUuNjU5IDMyLjcgMTE2LjcwOSAzNS4xMDA5IDExNi45NDhDMzcuNTAxOSAxMTcuMTg3IDM5LjkxMjYgMTE2LjYgNDEuOTMxIDExNS4yODRDNDMuMjgyIDExNC4zNzEgNDQuMzg4MSAxMTMuMTQzIDQ1LjE1MjcgMTExLjcwN0M0NS45MTc0IDExMC4yNzEgNDYuMzE3NSAxMDguNjcxIDQ2LjMxODEgMTA3LjA0NkM0Ni4zMTgxIDkwLjM1MjggNDYuMjg2MSA3My42NTk3IDQ2LjMxODEgNTYuOTY2NkM2MS42MTY4IDc1LjE4ODkgNzYuOTQ3NCA5My4zODU2IDkyLjMxIDExMS41NTdDOTQuNDQ0NCAxMTMuNzA4IDk3LjA4NzUgMTE1LjI5MSA5OS45OTcgMTE2LjE2MkMxMDIuOTA2IDExNy4wMzIgMTA1Ljk4OSAxMTcuMTYyIDEwOC45NjIgMTE2LjUzOUMxMTEuMDYxIDExNi4xMjggMTEyLjk3MyAxMTUuMDU3IDExNC40MTUgMTEzLjQ4NUMxMTUuODU3IDExMS45MTMgMTE2Ljc1NCAxMDkuOTIxIDExNi45NzQgMTA3LjgwNEMxMTcuMDA5IDg0LjI2ODEgMTE3LjAwOSA2MC43MzQzIDExNi45NzQgMzcuMjAyNUMxMTYuNzU0IDM0LjY5MDcgMTE1LjU5NSAzMi4zNTIyIDExMy43MjYgMzAuNjQ4NkMxMTEuODU4IDI4Ljk0NSAxMDkuNDE1IDI4IDEwNi44ODEgMjhDMTA0LjM0NiAyOCAxMDEuOTAzIDI4Ljk0NSAxMDAuMDM1IDMwLjY0ODZDOTguMTY2MyAzMi4zNTIyIDk3LjAwNzQgMzQuNjkwNyA5Ni43ODY5IDM3LjIwMjVDOTYuNzg2OSA1NC4xNjMyIDk2LjY4NDQgNzEuMTA0OCA5Ni43ODY5IDg4LjA1OTFDODEuNzYxNiA3MC40MzU4IDY2LjkyMTkgNTIuNjU5NiA1MS45NTQzIDM0Ljk3MjVDNDkuOTgxIDMyLjQ1NTQgNDcuMzY4NSAzMC41MDczIDQ0LjM4NjMgMjkuMzI5MUM0MS40MDQxIDI4LjE1MDkgMzguMTU5OSAyNy43ODUyIDM0Ljk4ODMgMjguMjY5OEMzMi41ODU3IDI4LjUzNTkgMzAuMzU4MyAyOS42NDkzIDI4LjcwOTkgMzEuNDA4NEMyNy4wNjE1IDMzLjE2NzUgMjYuMTAxIDM1LjQ1NTkgMjYuMDAyNiAzNy44NTg4WiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODFfMjgyNSIgeDE9IjEwIiB5MT0iMTUuNSIgeDI9IjE0NCIgeTI9IjEzMS41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwMEM1NEEiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDE5NjM5Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "endpoints"], ["spec", "haproxy"], ["spec", "haproxy", "replicas"], ["spec", "haproxy", "resources"], ["spec", "haproxy", "resourcesPreset"], ["spec", "nginx"], ["spec", "nginx", "replicas"], ["spec", "nginx", "resources"], ["spec", "nginx", "resourcesPreset"]] + secrets: + exclude: [] + include: [] diff --git a/packages/system/http-cache-rd/templates/cozyrd.yaml b/packages/system/http-cache-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/http-cache-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/http-cache-rd/values.yaml b/packages/system/http-cache-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/http-cache-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/info-rd/Chart.yaml b/packages/system/info-rd/Chart.yaml new file mode 100644 index 00000000..2f354177 --- /dev/null +++ b/packages/system/info-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: info-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/info-rd/Makefile b/packages/system/info-rd/Makefile new file mode 100644 index 00000000..7cfed08d --- /dev/null +++ b/packages/system/info-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=info-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/info-rd/cozyrds/info.yaml b/packages/system/info-rd/cozyrds/info.yaml new file mode 100644 index 00000000..43cda091 --- /dev/null +++ b/packages/system/info-rd/cozyrds/info.yaml @@ -0,0 +1,37 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: info +spec: + application: + kind: Info + plural: infos + singular: info + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{}} + release: + prefix: "" + labels: + cozystack.io/ui: "true" + internal.cozystack.io/tenantmodule: "true" + chart: + name: info + sourceRef: + kind: HelmRepository + name: cozystack-extra + namespace: cozy-public + dashboard: + name: info + category: Administration + singular: Info + plural: Info + description: Info + module: true + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX3JhZGlhbF8xNDRfMykiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzE0NF8zKSI+CjxwYXRoIGQ9Ik03Ny42NDA3IDk3LjA4NDRMODIuODMzIDk3LjM2MDRWMTA0LjYzN0g2MS4xNzI4Vjk3LjcxOTdMNjQuMTc3MSA5Ny40NDk1QzY1LjgxMDEgOTcuMjY4NCA2Ni44MTA2IDk2LjcxOTMgNjYuODEwNiA5NC41MzQzVjY5LjIzMTRDNjYuODEwNiA2Ny4yMjE3IDY2LjI3MDEgNjYuNTg2NCA2NC41MzY1IDY2LjU4NjRMNjEuMzU2OCA2Ni40MDgxVjU4Ljg1ODRINzcuNjQ2NUw3Ny42NDA3IDk3LjA4NDRaTTcxLjI3MjYgMzkuMzYzQzc1LjI4MDQgMzkuMzYzIDc4LjE4NyA0Mi4zNzMxIDc4LjE4NyA0Ni4xODgzQzc4LjE4NyA1MC4wMTQ5IDc1LjI3MTggNTIuODM4MSA3MS4xNzc4IDUyLjgzODFDNjYuOTk3NSA1Mi44MzgxIDY0LjI2NjMgNTAuMDE0OSA2NC4yNjYzIDQ2LjE4ODNDNjQuMjY2MyA0Mi4zNzMxIDY2Ljk5NzUgMzkuMzYzIDcxLjI3MjYgMzkuMzYzWk03MiAxMThDNDYuNjM2OCAxMTggMjYgOTcuMzYzMiAyNiA3MkMyNiA0Ni42MzY4IDQ2LjYzNjggMjYgNzIgMjZDOTcuMzU3NSAyNiAxMTggNDYuNjM2OCAxMTggNzJDMTE4IDk3LjM2MzIgOTcuMzU3NSAxMTggNzIgMTE4Wk03MiAzNC42MjVDNTEuMzkyIDM0LjYyNSAzNC42MjUgNTEuMzkyIDM0LjYyNSA3MkMzNC42MjUgOTIuNjA4IDUxLjM5MiAxMDkuMzc1IDcyIDEwOS4zNzVDOTIuNjA4IDEwOS4zNzUgMTA5LjM3NSA5Mi42MDggMTA5LjM3NSA3MkMxMDkuMzc1IDUxLjM5MiA5Mi42MDggMzQuNjI1IDcyIDM0LjYyNVoiIGZpbGw9IndoaXRlIi8+CjwvZz4KPGRlZnM+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQwX3JhZGlhbF8xNDRfMyIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgxLjMyMjk4ZS0wNSAtNy41MDAwMSkgcm90YXRlKDQ0LjcxNzgpIHNjYWxlKDIxNS4zMTcgMzEyLjQ1NSkiPgo8c3RvcCBzdG9wLWNvbG9yPSIjMDBCNUU3Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwMzk4NCIvPgo8L3JhZGlhbEdyYWRpZW50Pgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzE0NF8zIj4KPHJlY3Qgd2lkdGg9IjkyIiBoZWlnaHQ9IjkyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYgMjYpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg== + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"]] + secrets: + exclude: [] + include: + - resourceNames: + - kubeconfig-{{ .namespace }} + - "{{ .namespace }}" diff --git a/packages/system/info-rd/templates/cozyrd.yaml b/packages/system/info-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/info-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/info-rd/values.yaml b/packages/system/info-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/info-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/ingress-rd/Chart.yaml b/packages/system/ingress-rd/Chart.yaml new file mode 100644 index 00000000..2e1b77d7 --- /dev/null +++ b/packages/system/ingress-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: ingress-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/ingress-rd/Makefile b/packages/system/ingress-rd/Makefile new file mode 100644 index 00000000..82976cc6 --- /dev/null +++ b/packages/system/ingress-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=ingress-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/ingress-rd/cozyrds/ingress.yaml b/packages/system/ingress-rd/cozyrds/ingress.yaml new file mode 100644 index 00000000..2e00f6ed --- /dev/null +++ b/packages/system/ingress-rd/cozyrds/ingress.yaml @@ -0,0 +1,39 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: ingress +spec: + application: + kind: Ingress + plural: ingresses + singular: ingress + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"cloudflareProxy":{"description":"Restoring original visitor IPs when Cloudflare proxied is enabled.","type":"boolean","default":false},"replicas":{"description":"Number of ingress-nginx replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each ingress-nginx replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"whitelist":{"description":"List of client networks.","type":"array","default":[],"items":{"type":"string"}}}} + release: + prefix: "" + labels: + cozystack.io/ui: "true" + internal.cozystack.io/tenantmodule: "true" + chart: + name: ingress + sourceRef: + kind: HelmRepository + name: cozystack-extra + namespace: cozy-public + dashboard: + category: Administration + singular: Ingress + plural: Ingress + name: ingress + description: NGINX Ingress Controller + module: true + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODRfMzIyOSkiLz4KPHBhdGggZD0iTTg2LjkyNzQgMzcuMTA3NEgxN1YxMDcuMDM1SDg2LjkyNzRWMzcuMTA3NFoiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iNiIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTEyNy42NDMgMjlIMTA3LjQ1NVY0OS4xODgzSDEyNy42NDNWMjlaIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0xMjcuNjQzIDYxLjcyNjZIMTA3LjQ1NVY4MS45MTQ5SDEyNy42NDNWNjEuNzI2NloiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTEyNy42NDMgOTQuNDUyMUgxMDcuNDU1VjExNC42NEgxMjcuNjQzVjk0LjQ1MjFaIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik04OC41MTM3IDcyLjA3MTNIMTA2LjI3IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik04Ny41Njc0IDgwLjQyNDhMMTA3LjczIDk1Ljc4MDUiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTg3LjU2NzQgNjMuNzE4MUwxMDcuNzMgNDguMzYyMyIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4NF8zMjI5IiB4MT0iMTAiIHkxPSIxNS41IiB4Mj0iMTQ0IiB5Mj0iMTMxLjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzAwREE1MyIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMwMDk2MzkiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "whitelist"], ["spec", "cloudflareProxy"], ["spec", "resources"], ["spec", "resourcesPreset"]] + secrets: + exclude: [] + include: [] + services: + exclude: [] + include: + - resourceNames: + - "{{ slice .namespace 7 }}-ingress-controller" diff --git a/packages/system/ingress-rd/templates/cozyrd.yaml b/packages/system/ingress-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/ingress-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/ingress-rd/values.yaml b/packages/system/ingress-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/ingress-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/kafka-rd/Chart.yaml b/packages/system/kafka-rd/Chart.yaml new file mode 100644 index 00000000..87b84e32 --- /dev/null +++ b/packages/system/kafka-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: kafka-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/kafka-rd/Makefile b/packages/system/kafka-rd/Makefile new file mode 100644 index 00000000..3c333dcf --- /dev/null +++ b/packages/system/kafka-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=kafka-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/kafka-rd/cozyrds/kafka.yaml b/packages/system/kafka-rd/cozyrds/kafka.yaml new file mode 100644 index 00000000..7009d241 --- /dev/null +++ b/packages/system/kafka-rd/cozyrds/kafka.yaml @@ -0,0 +1,40 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: kafka +spec: + application: + kind: Kafka + plural: kafkas + singular: kafka + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"kafka":{"description":"Kafka configuration.","type":"object","default":{},"required":["replicas","resourcesPreset","size","storageClass"],"properties":{"replicas":{"description":"Number of Kafka replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume size for Kafka.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the Kafka data.","type":"string","default":""}}},"topics":{"description":"Topics configuration.","type":"array","default":[],"items":{"type":"object","required":["config","name","partitions","replicas"],"properties":{"config":{"description":"Topic configuration.","type":"object","x-kubernetes-preserve-unknown-fields":true},"name":{"description":"Topic name.","type":"string"},"partitions":{"description":"Number of partitions.","type":"integer"},"replicas":{"description":"Number of replicas.","type":"integer"}}}},"zookeeper":{"description":"ZooKeeper configuration.","type":"object","default":{},"required":["replicas","resourcesPreset","size","storageClass"],"properties":{"replicas":{"description":"Number of ZooKeeper replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume size for ZooKeeper.","default":"5Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the ZooKeeper data.","type":"string","default":""}}}}} + release: + prefix: kafka- + labels: + cozystack.io/ui: "true" + chart: + name: kafka + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: Kafka + plural: Kafka + description: Managed Kafka service + tags: + - messaging + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODFfMjgyMCkiLz4KPHBhdGggZD0iTTkxLjAzMDcgNzcuODE4NUM4Ni44NTc3IDc3LjgxODUgODMuMTE2NiA3OS42ODE4IDgwLjU1NDcgODIuNjE1NEw3My45OTAxIDc3LjkzMTVDNzQuNjg2OSA3NS45OTc4IDc1LjA4NyA3My45MjE1IDc1LjA4NyA3MS43NDgyQzc1LjA4NyA2OS42MTI2IDc0LjcwMDggNjcuNTcxMSA3NC4wMjY5IDY1LjY2Nkw4MC41NzY5IDYxLjAzMThDODMuMTM4NSA2My45NTA1IDg2Ljg2OTkgNjUuODAzNyA5MS4wMzA3IDY1LjgwMzdDOTguNzMyOCA2NS44MDM3IDEwNSA1OS40ODg0IDEwNSA1MS43MjQ3QzEwNSA0My45NjEgOTguNzMyOCAzNy42NDU3IDkxLjAzMDcgMzcuNjQ1N0M4My4zMjg1IDM3LjY0NTcgNzcuMDYxNCA0My45NjEgNzcuMDYxNCA1MS43MjQ3Qzc3LjA2MTQgNTMuMTE0MyA3Ny4yNjk3IDU0LjQ1NDMgNzcuNjQzNSA1NS43MjMzTDcxLjA4OTEgNjAuMzU5OEM2OC4zNTEyIDU2LjkzNjUgNjQuNDA5IDU0LjU0NjMgNTkuOTE3NCA1My44MTY2VjQ1Ljg1NTNDNjYuMjQ1MSA0NC41MTU4IDcxLjAxMjggMzguODQ5NSA3MS4wMTI4IDMyLjA3OUM3MS4wMTI4IDI0LjMxNTMgNjQuNzQ1NyAxOCA1Ny4wNDM1IDE4QzQ5LjM0MTQgMTggNDMuMDc0MiAyNC4zMTUzIDQzLjA3NDIgMzIuMDc5QzQzLjA3NDIgMzguNzU4OSA0Ny43MTg0IDQ0LjM1NTIgNTMuOTE5NiA0NS43OTAzVjUzLjg1NTFDNDUuNDU2NyA1NS4zNTIzIDM5IDYyLjc5NjEgMzkgNzEuNzQ4MkMzOSA4MC43NDQgNDUuNTIwNiA4OC4yMTUxIDU0LjA0NDYgODkuNjYxM1Y5OC4xNzcyQzQ3Ljc4MDEgOTkuNTY1IDQzLjA3NDIgMTA1LjE5NiA0My4wNzQyIDExMS45MjFDNDMuMDc0MiAxMTkuNjg1IDQ5LjM0MTQgMTI2IDU3LjA0MzUgMTI2QzY0Ljc0NTcgMTI2IDcxLjAxMjggMTE5LjY4NSA3MS4wMTI4IDExMS45MjFDNzEuMDEyOCAxMDUuMTk2IDY2LjMwNyA5OS41NjUgNjAuMDQyNCA5OC4xNzcyVjg5LjY2MTFDNjQuMzU2OSA4OC45Mjg2IDY4LjI2MDEgODYuNjQwNyA3MS4wMjUyIDgzLjIyMzRMNzcuNjMzNyA4Ny45Mzc2Qzc3LjI2NjkgODkuMTk1MiA3Ny4wNjE0IDkwLjUyMTkgNzcuMDYxNCA5MS44OTc1Qzc3LjA2MTQgOTkuNjYxMiA4My4zMjg1IDEwNS45NzYgOTEuMDMwNyAxMDUuOTc2Qzk4LjczMjggMTA1Ljk3NiAxMDUgOTkuNjYxMiAxMDUgOTEuODk3NUMxMDUgODQuMTMzOCA5OC43MzI4IDc3LjgxODUgOTEuMDMwNyA3Ny44MTg1Wk05MS4wMzA3IDQ0Ljg5ODVDOTQuNzY1NiA0NC44OTg1IDk3LjgwMzQgNDcuOTYxNSA5Ny44MDM0IDUxLjcyNDdDOTcuODAzNCA1NS40ODc5IDk0Ljc2NTYgNTguNTUwNiA5MS4wMzA3IDU4LjU1MDZDODcuMjk1OCA1OC41NTA2IDg0LjI1OCA1NS40ODc5IDg0LjI1OCA1MS43MjQ3Qzg0LjI1OCA0Ny45NjE1IDg3LjI5NTggNDQuODk4NSA5MS4wMzA3IDQ0Ljg5ODVaTTUwLjI3MDUgMzIuMDc5QzUwLjI3MDUgMjguMzE1OCA1My4zMDg2IDI1LjI1MzEgNTcuMDQzNSAyNS4yNTMxQzYwLjc3ODUgMjUuMjUzMSA2My44MTYzIDI4LjMxNTggNjMuODE2MyAzMi4wNzlDNjMuODE2MyAzNS44NDIyIDYwLjc3ODUgMzguOTA0OSA1Ny4wNDM1IDM4LjkwNDlDNTMuMzA4NiAzOC45MDQ5IDUwLjI3MDUgMzUuODQyMiA1MC4yNzA1IDMyLjA3OVpNNjMuODE2MyAxMTEuOTIxQzYzLjgxNjMgMTE1LjY4NCA2MC43Nzg1IDExOC43NDcgNTcuMDQzNSAxMTguNzQ3QzUzLjMwODYgMTE4Ljc0NyA1MC4yNzA1IDExNS42ODQgNTAuMjcwNSAxMTEuOTIxQzUwLjI3MDUgMTA4LjE1OCA1My4zMDg2IDEwNS4wOTUgNTcuMDQzNSAxMDUuMDk1QzYwLjc3ODUgMTA1LjA5NSA2My44MTYzIDEwOC4xNTggNjMuODE2MyAxMTEuOTIxWk01Ny4wNDMgODEuMjY4MUM1MS44MzM5IDgxLjI2ODEgNDcuNTk2MiA3Ni45OTggNDcuNTk2MiA3MS43NDgyQzQ3LjU5NjIgNjYuNDk4MiA1MS44MzM5IDYyLjIyNzMgNTcuMDQzIDYyLjIyNzNDNjIuMjUxOSA2Mi4yMjczIDY2LjQ4OTUgNjYuNDk4MiA2Ni40ODk1IDcxLjc0ODJDNjYuNDg5NSA3Ni45OTggNjIuMjUxOSA4MS4yNjgxIDU3LjA0MyA4MS4yNjgxWk05MS4wMzA3IDk4LjcyMzdDODcuMjk1OCA5OC43MjM3IDg0LjI1OCA5NS42NjA3IDg0LjI1OCA5MS44OTc1Qzg0LjI1OCA4OC4xMzQzIDg3LjI5NTggODUuMDcxNiA5MS4wMzA3IDg1LjA3MTZDOTQuNzY1NiA4NS4wNzE2IDk3LjgwMzQgODguMTM0MyA5Ny44MDM0IDkxLjg5NzVDOTcuODAzNCA5NS42NjA3IDk0Ljc2NTYgOTguNzIzNyA5MS4wMzA3IDk4LjcyMzdaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4MV8yODIwIiB4MT0iMTQwIiB5MT0iMTMwLjUiIHgyPSI0IiB5Mj0iOS40OTk5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcC8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzQzNDE0MSIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "external"], ["spec", "topics"], ["spec", "kafka"], ["spec", "kafka", "replicas"], ["spec", "kafka", "resources"], ["spec", "kafka", "resourcesPreset"], ["spec", "kafka", "size"], ["spec", "kafka", "storageClass"], ["spec", "zookeeper"], ["spec", "zookeeper", "replicas"], ["spec", "zookeeper", "resources"], ["spec", "zookeeper", "resourcesPreset"], ["spec", "zookeeper", "size"], ["spec", "zookeeper", "storageClass"]] + secrets: + exclude: [] + include: + - resourceNames: + - kafka-{{ .name }}-clients-ca + services: + exclude: [] + include: + - resourceNames: + - kafka-{{ .name }}-kafka-bootstrap diff --git a/packages/system/kafka-rd/templates/cozyrd.yaml b/packages/system/kafka-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/kafka-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/kafka-rd/values.yaml b/packages/system/kafka-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/kafka-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/kubernetes-rd/Chart.yaml b/packages/system/kubernetes-rd/Chart.yaml new file mode 100644 index 00000000..8fad1a45 --- /dev/null +++ b/packages/system/kubernetes-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: kubernetes-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/kubernetes-rd/Makefile b/packages/system/kubernetes-rd/Makefile new file mode 100644 index 00000000..45969603 --- /dev/null +++ b/packages/system/kubernetes-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=kubernetes-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml new file mode 100644 index 00000000..05ed110f --- /dev/null +++ b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml @@ -0,0 +1,46 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: kubernetes +spec: + application: + kind: Kubernetes + singular: kubernetes + plural: kuberneteses + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"addons":{"description":"Cluster addons configuration.","type":"object","default":{},"required":["certManager","cilium","coredns","fluxcd","gatewayAPI","gpuOperator","ingressNginx","monitoringAgents","velero","verticalPodAutoscaler"],"properties":{"certManager":{"description":"Cert-manager addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable cert-manager.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"cilium":{"description":"Cilium CNI plugin.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"coredns":{"description":"CoreDNS addon.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"fluxcd":{"description":"FluxCD GitOps operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable FluxCD.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"gatewayAPI":{"description":"Gateway API addon.","type":"object","default":{},"required":["enabled"],"properties":{"enabled":{"description":"Enable Gateway API.","type":"boolean","default":false}}},"gpuOperator":{"description":"NVIDIA GPU Operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable GPU Operator.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"ingressNginx":{"description":"Ingress-NGINX controller.","type":"object","default":{},"required":["enabled","exposeMethod","valuesOverride"],"properties":{"enabled":{"description":"Enable the controller (requires nodes labeled `ingress-nginx`).","type":"boolean","default":false},"exposeMethod":{"description":"Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`.","type":"string","default":"Proxied"},"hosts":{"description":"Domains routed to this tenant cluster when `exposeMethod` is `Proxied`.","type":"array","default":[],"items":{"type":"string"}},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"monitoringAgents":{"description":"Monitoring agents.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable monitoring agents.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"velero":{"description":"Velero backup/restore addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable Velero.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"verticalPodAutoscaler":{"description":"Vertical Pod Autoscaler.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}}}},"controlPlane":{"description":"Kubernetes control-plane configuration.","type":"object","default":{},"required":["apiServer","controllerManager","konnectivity","replicas","scheduler"],"properties":{"apiServer":{"description":"API Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for API Server.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"medium","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"controllerManager":{"description":"Controller Manager configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Controller Manager.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"konnectivity":{"description":"Konnectivity configuration.","type":"object","default":{},"required":["server"],"properties":{"server":{"description":"Konnectivity Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Konnectivity.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"replicas":{"description":"Number of control-plane replicas.","type":"integer","default":2},"scheduler":{"description":"Scheduler configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Scheduler.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"host":{"description":"External hostname for Kubernetes cluster. Defaults to `.` if empty.","type":"string","default":""},"nodeGroups":{"description":"Worker nodes configuration map.","type":"object","default":{"md0":{"ephemeralStorage":"20Gi","gpus":[],"instanceType":"u1.medium","maxReplicas":10,"minReplicas":0,"resources":{},"roles":["ingress-nginx"]}},"additionalProperties":{"type":"object","required":["ephemeralStorage","instanceType","maxReplicas","minReplicas","resources"],"properties":{"ephemeralStorage":{"description":"Ephemeral storage size.","default":"20Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"gpus":{"description":"List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"description":"Name of GPU, such as \"nvidia.com/AD102GL_L40S\".","type":"string"}}}},"instanceType":{"description":"Virtual machine instance type.","type":"string","default":"u1.medium"},"maxReplicas":{"description":"Maximum number of replicas.","type":"integer","default":10},"minReplicas":{"description":"Minimum number of replicas.","type":"integer","default":0},"resources":{"description":"CPU and memory resources for each worker node.","type":"object","properties":{"cpu":{"description":"CPU available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"roles":{"description":"List of node roles.","type":"array","items":{"type":"string"}}}}},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated"},"version":{"description":"Kubernetes major.minor version to deploy","type":"string","default":"v1.33","enum":["v1.33","v1.32","v1.31","v1.30","v1.29","v1.28"]}}} + release: + prefix: kubernetes- + labels: + cozystack.io/ui: "true" + chart: + name: kubernetes + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: IaaS + singular: Kubernetes + plural: Kubernetes + weight: 40 + description: Managed Kubernetes service + tags: + - compute + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODFfMjg0NSkiLz4KPHBhdGggZD0iTTcxLjk5NjggMTlDNzAuMzAzOSAxOS4wMDAyIDY4LjkzMTIgMjAuNTMyMiA2OC45MzE0IDIyLjQyMjFDNjguOTMxNCAyMi40NTExIDY4LjkzNzMgMjIuNDc4OCA2OC45Mzc5IDIyLjUwNzZDNjguOTM1NCAyMi43NjQ0IDY4LjkyMzEgMjMuMDczNyA2OC45MzE0IDIzLjI5NzNDNjguOTcxNyAyNC4zODczIDY5LjIwODIgMjUuMjIxNiA2OS4zNTA2IDI2LjIyNThDNjkuNjA4NCAyOC4zNzUyIDY5LjgyNDUgMzAuMTU2OSA2OS42OTEyIDMxLjgxM0M2OS41NjE1IDMyLjQzNzUgNjkuMTAzNyAzMy4wMDg2IDY4LjY5NTYgMzMuNDA1Nkw2OC42MjM1IDM0LjcwODZDNjYuNzgzOSAzNC44NjE3IDY0LjkzMTkgMzUuMTQyMSA2My4wODIxIDM1LjU2NDFDNTUuMTIyNiAzNy4zNzk4IDQ4LjI2OTUgNDEuNDk5MSA0My4wNTIgNDcuMDYwOUM0Mi43MTM0IDQ2LjgyODggNDIuMTIxMSA0Ni40MDE5IDQxLjk0NSA0Ni4yNzEyQzQxLjM5NzcgNDYuMzQ1NCA0MC44NDQ1IDQ2LjUxNTEgNDAuMTI0MSA0Ni4wOTM1QzM4Ljc1MjIgNDUuMTY1NyAzNy41MDI4IDQzLjg4NTEgMzUuOTkxIDQyLjM0MjRDMzUuMjk4MiA0MS42MDQ0IDM0Ljc5NjYgNDAuOTAxOCAzMy45NzM1IDQwLjE5MDRDMzMuNzg2NiA0MC4wMjg5IDMzLjUwMTQgMzkuODEwNCAzMy4yOTIzIDM5LjY0NDJDMzIuNjQ4OSAzOS4xMjg4IDMxLjg5IDM4Ljg2IDMxLjE1NyAzOC44MzQ4QzMwLjIxNDcgMzguODAyNCAyOS4zMDc1IDM5LjE3MjUgMjguNzEzOCAzOS45MjA2QzI3LjY1ODQgNDEuMjUwNiAyNy45OTYzIDQzLjI4MzMgMjkuNDY3MSA0NC40NjE0QzI5LjQ4MiA0NC40NzM0IDI5LjQ5NzkgNDQuNDgyNyAyOS41MTI5IDQ0LjQ5NDNDMjkuNzE1IDQ0LjY1ODkgMjkuOTYyNSA0NC44Njk4IDMwLjE0ODMgNDUuMDA3NkMzMS4wMjE3IDQ1LjY1NTUgMzEuODE5NSA0NS45ODcyIDMyLjY4OTcgNDYuNTAxNUMzNC41MjMxIDQ3LjYzOTEgMzYuMDQzIDQ4LjU4MjMgMzcuMjQ4NiA0OS43MTk2QzM3LjcxOTQgNTAuMjIzNyAzNy44MDE2IDUxLjExMjIgMzcuODY0MyA1MS40OTY0TDM4Ljg0NjggNTIuMzc4MkMzMy41ODcyIDYwLjMzMDggMzEuMTUzIDcwLjE1MzkgMzIuNTkxNSA4MC4xNjI3TDMxLjMwNzcgODAuNTM3OEMzMC45NjkzIDgwLjk3NjggMzAuNDkxMiA4MS42Njc2IDI5Ljk5MTEgODEuODczOEMyOC40MTM4IDgyLjM3MjkgMjYuNjM4NyA4Mi41NTYyIDI0LjQ5NTYgODIuNzgxOUMyMy40ODk0IDgyLjg2NiAyMi42MjEzIDgyLjgxNTggMjEuNTU0NiA4My4wMTg4QzIxLjMxOTggODMuMDYzNSAyMC45OTI3IDgzLjE0OTEgMjAuNzM1OCA4My4yMDk3QzIwLjcyNjkgODMuMjExNiAyMC43MTg2IDgzLjIxNDIgMjAuNzA5NiA4My4yMTYyQzIwLjY5NTYgODMuMjE5NSAyMC42NzcyIDgzLjIyNjMgMjAuNjYzOCA4My4yMjk0QzE4Ljg1NyA4My42NjggMTcuNjk2MyA4NS4zMzY1IDE4LjA2OTkgODYuOTgwNUMxOC40NDM3IDg4LjYyNDggMjAuMjA4NiA4OS42MjQ4IDIyLjAyNjIgODkuMjMxMkMyMi4wMzkzIDg5LjIyODIgMjIuMDU4NCA4OS4yMjc3IDIyLjA3MiA4OS4yMjQ2QzIyLjA5MjYgODkuMjE5OSAyMi4xMTA2IDg5LjIwOTkgMjIuMTMxIDg5LjIwNDlDMjIuMzg0NCA4OS4xNDkgMjIuNzAxOSA4OS4wODY4IDIyLjkyMzYgODkuMDI3MkMyMy45NzIzIDg4Ljc0NTEgMjQuNzMxOCA4OC4zMzA2IDI1LjY3NDYgODcuOTY3N0MyNy43MDI5IDg3LjIzNjggMjkuMzgyOCA4Ni42MjYyIDMxLjAxOTUgODYuMzg4M0MzMS43MDMgODYuMzM0NSAzMi40MjMyIDg2LjgxMiAzMi43ODE0IDg3LjAxMzRMMzQuMTE3NyA4Ni43ODMxQzM3LjE5MjYgOTYuMzYxMyA0My42MzY2IDEwNC4xMDMgNTEuNzk2MyAxMDguOTYxTDUxLjIzOTYgMTEwLjMwM0M1MS40NDAzIDExMC44MjQgNTEuNjYxNiAxMTEuNTMgNTEuNTEyMSAxMTIuMDQ1QzUwLjkxNzEgMTEzLjU5NSA0OS44OTggMTE1LjIzMSA0OC43Mzc0IDExNy4wNTVDNDguMTc1NSAxMTcuODk4IDQ3LjYwMDQgMTE4LjU1MiA0Ny4wOTM0IDExOS41MTZDNDYuOTcyIDExOS43NDcgNDYuODE3NSAxMjAuMTAyIDQ2LjcwMDQgMTIwLjM0NkM0NS45MTI1IDEyMi4wMzkgNDYuNDkwNCAxMjMuOTkgNDguMDAzOCAxMjQuNzIyQzQ5LjUyNjggMTI1LjQ1OSA1MS40MTcxIDEyNC42ODIgNTIuMjM1MiAxMjIuOTg1QzUyLjIzNjQgMTIyLjk4MiA1Mi4yNDA2IDEyMi45OCA1Mi4yNDE3IDEyMi45NzhDNTIuMjQyNiAxMjIuOTc2IDUyLjI0MDkgMTIyLjk3MyA1Mi4yNDE3IDEyMi45NzFDNTIuMzU4MiAxMjIuNzMxIDUyLjUyMzMgMTIyLjQxNSA1Mi42MjE2IDEyMi4xODhDNTMuMDU2IDEyMS4xODkgNTMuMjAwNSAxMjAuMzMyIDUzLjUwNTkgMTE5LjM2NUM1NC4zMTcgMTE3LjMxOCA1NC43NjI2IDExNS4xNyA1NS44NzkxIDExMy44MzJDNTYuMTg0OSAxMTMuNDY2IDU2LjY4MzMgMTEzLjMyNSA1Ny4yMDAxIDExMy4xODZMNTcuODk0NCAxMTEuOTIyQzY1LjAwOCAxMTQuNjY1IDcyLjk3MDUgMTE1LjQwMiA4MC45MjQ1IDExMy41ODdDODIuNzM5MSAxMTMuMTczIDg0LjQ5MDggMTEyLjYzNyA4Ni4xODQzIDExMS45OTRDODYuMzc5NCAxMTIuMzQyIDg2Ljc0MiAxMTMuMDExIDg2LjgzOTMgMTEzLjE3OUM4Ny4zNjQ0IDExMy4zNTEgODcuOTM3NyAxMTMuNDM5IDg4LjQwNDcgMTE0LjEzM0M4OS4yNDAxIDExNS41NjcgODkuODExNCAxMTcuMjYzIDkwLjUwNzMgMTE5LjMxMkM5MC44MTI4IDEyMC4yNzkgOTAuOTYzOCAxMjEuMTM2IDkxLjM5ODEgMTIyLjEzNkM5MS40OTcxIDEyMi4zNjMgOTEuNjYxNCAxMjIuNjg0IDkxLjc3OCAxMjIuOTI1QzkyLjU5NDQgMTI0LjYyOCA5NC40OTA3IDEyNS40MDcgOTYuMDE1OSAxMjQuNjY5Qzk3LjUyOTIgMTIzLjkzNyA5OC4xMDc3IDEyMS45ODYgOTcuMzE5NCAxMjAuMjkzQzk3LjIwMjMgMTIwLjA0OSA5Ny4wNDEyIDExOS42OTUgOTYuOTE5OCAxMTkuNDY0Qzk2LjQxMjcgMTE4LjQ5OSA5NS44Mzc3IDExNy44NTIgOTUuMjc1OCAxMTcuMDA5Qzk0LjExNTIgMTE1LjE4NSA5My4xNTI2IDExMy42NyA5Mi41NTc1IDExMi4xMkM5Mi4zMDg3IDExMS4zMiA5Mi41OTk1IDExMC44MjMgOTIuNzkzMyAxMTAuMzAzQzkyLjY3NzIgMTEwLjE3IDkyLjQyODggMTA5LjQxNCA5Mi4yODI0IDEwOS4wNTlDMTAwLjc2MiAxMDQuMDI5IDEwNy4wMTcgOTUuOTk4NSAxMDkuOTU1IDg2LjcyMzlDMTEwLjM1MSA4Ni43ODY1IDExMS4wNDEgODYuOTA5MSAxMTEuMjY1IDg2Ljk1NDJDMTExLjcyNiA4Ni42NDg3IDExMi4xNDkgODYuMjUwMSAxMTIuOTgxIDg2LjMxNTlDMTE0LjYxNyA4Ni41NTM3IDExNi4yOTcgODcuMTY0NSAxMTguMzI2IDg3Ljg5NTNDMTE5LjI2OCA4OC4yNTgxIDEyMC4wMjggODguNjc5MyAxMjEuMDc3IDg4Ljk2MTRDMTIxLjI5OCA4OS4wMjEgMTIxLjYxNiA4OS4wNzY2IDEyMS44NjkgODkuMTMyNUMxMjEuODg5IDg5LjEzNzUgMTIxLjkwOCA4OS4xNDc1IDEyMS45MjggODkuMTUyMkMxMjEuOTQyIDg5LjE1NTMgMTIxLjk2MSA4OS4xNTU4IDEyMS45NzQgODkuMTU4OEMxMjMuNzkyIDg5LjU1MiAxMjUuNTU3IDg4LjU1MjYgMTI1LjkzIDg2LjkwODFDMTI2LjMwMyA4NS4yNjQxIDEyNS4xNDMgODMuNTk1MiAxMjMuMzM2IDgzLjE1N0MxMjMuMDc0IDgzLjA5NyAxMjIuNzAxIDgyLjk5NSAxMjIuNDQ2IDgyLjk0NjVDMTIxLjM3OSA4Mi43NDM1IDEyMC41MTEgODIuNzkzNSAxMTkuNTA1IDgyLjcwOTVDMTE3LjM2MSA4Mi40ODM5IDExNS41ODYgODIuMzAwNCAxMTQuMDA5IDgxLjgwMTRDMTEzLjM2NiA4MS41NTA3IDExMi45MDggODAuNzgxOSAxMTIuNjg2IDgwLjQ2NTVMMTExLjQ0OCA4MC4xMDM1QzExMi4wOSA3NS40MzggMTExLjkxNyA3MC41ODI1IDExMC44MDYgNjUuNzI0M0MxMDkuNjg1IDYwLjgyMDggMTA3LjcwNCA1Ni4zMzYxIDEwNS4wNjIgNTIuMzg0OEMxMDUuMzc5IDUyLjA5NDggMTA1Ljk3OSA1MS41NjEyIDEwNi4xNDkgNTEuNDA0M0MxMDYuMTk5IDUwLjg1MTcgMTA2LjE1NiA1MC4yNzIyIDEwNi43MjUgNDkuNjYwM0MxMDcuOTMxIDQ4LjUyMyAxMDkuNDUxIDQ3LjU3OTkgMTExLjI4NCA0Ni40NDIzQzExMi4xNTQgNDUuOTI3OSAxMTIuOTU5IDQ1LjU5NjQgMTEzLjgzMiA0NC45NDg0QzExNC4wMyA0NC44MDE5IDExNC4yOTkgNDQuNTY5OSAxMTQuNTA3IDQ0LjQwMjJDMTE1Ljk3NyA0My4yMjM3IDExNi4zMTYgNDEuMTkxMSAxMTUuMjYgMzkuODYxNEMxMTQuMjA0IDM4LjUzMTcgMTEyLjE1OSAzOC40MDY1IDExMC42ODggMzkuNTg1QzExMC40NzkgMzkuNzUxNiAxMTAuMTk1IDM5Ljk2ODggMTEwLjAwNyA0MC4xMzEyQzEwOS4xODQgNDAuODQyNiAxMDguNjc2IDQxLjU0NTIgMTA3Ljk4MyA0Mi4yODMyQzEwNi40NzEgNDMuODI1OSAxMDUuMjIyIDQ1LjExMyAxMDMuODUgNDYuMDQwOUMxMDMuMjU1IDQ2LjM4ODUgMTAyLjM4NSA0Ni4yNjgyIDEwMS45OSA0Ni4yNDQ5TDEwMC44MjQgNDcuMDgwNkM5NC4xNzUzIDQwLjA3NjMgODUuMTIzNSAzNS41OTgyIDc1LjM3NjYgMzQuNzI4M0M3NS4zNDk0IDM0LjMxNzkgNzUuMzEzNyAzMy41NzYxIDc1LjMwNDYgMzMuMzUyOUM3NC45MDU2IDMyLjk2OTMgNzQuNDIzNSAzMi42NDE4IDc0LjMwMjQgMzEuODEzQzc0LjE2OTEgMzAuMTU2OSA3NC4zOTE3IDI4LjM3NTIgNzQuNjQ5NiAyNi4yMjU4Qzc0Ljc5MTkgMjUuMjIxNiA3NS4wMjg0IDI0LjM4NzMgNzUuMDY4OCAyMy4yOTczQzc1LjA3OCAyMy4wNDk1IDc1LjA2MzIgMjIuNjkgNzUuMDYyMiAyMi40MjIxQzc1LjA2MiAyMC41MzIyIDczLjY4OTggMTguOTk5OCA3MS45OTY4IDE5Wk02OC4xNTg1IDQyLjg4ODZMNjcuMjQ4IDU5LjA0NDdMNjcuMTgyNSA1OS4wNzc2QzY3LjEyMTQgNjAuNTIyOSA2NS45Mzc1IDYxLjY3NyA2NC40ODM5IDYxLjY3N0M2My44ODg0IDYxLjY3NyA2My4zMzg4IDYxLjQ4NDkgNjIuODkyMiA2MS4xNTcxTDYyLjg2NiA2MS4xNzAzTDQ5LjY4MDcgNTEuNzc5NEM1My43MzMxIDQ3Ljc3NTkgNTguOTE2NCA0NC44MTcyIDY0Ljg5IDQzLjQ1NDZDNjUuOTgxMiA0My4yMDU2IDY3LjA3MTkgNDMuMDIwOSA2OC4xNTg1IDQyLjg4ODZaTTc1Ljg0MTcgNDIuODg4NkM4Mi44MTU5IDQzLjc1MDQgODkuMjY1NyA0Ni45MjMyIDk0LjIwODEgNTEuNzg2TDgxLjEwOCA2MS4xMTc2TDgxLjA2MjEgNjEuMDk3OUM3OS44OTk0IDYxLjk1MTIgNzguMjYxMSA2MS43Mzk0IDc3LjM1NDggNjAuNTk3OEM3Ni45ODM1IDYwLjEzMDEgNzYuNzg4NyA1OS41ODAxIDc2Ljc2NTMgNTkuMDI0OUw3Ni43NTIyIDU5LjAxODRMNzUuODQxNyA0Mi44ODg2Wk00NC44OTkxIDU3LjgxNEw1Ni45MzgyIDY4LjYzM0w1Ni45MjUxIDY4LjY5ODhDNTguMDExNyA2OS42NDc5IDU4LjE3MiA3MS4yOTQ5IDU3LjI2NTcgNzIuNDM2OEM1Ni44OTQ0IDcyLjkwNDUgNTYuMzk3NSA3My4yMTgyIDU1Ljg2MzkgNzMuMzY0N0w1NS44NTA4IDczLjQxNzNMNDAuNDE4OCA3Ny44OTIzQzM5LjYzMzQgNzAuNjc2NSA0MS4zMjYxIDYzLjY2MjEgNDQuODk5MSA1Ny44MTRaTTk5LjAwOTQgNTcuODIwNkMxMDAuNzk4IDYwLjczMzYgMTAyLjE1MyA2My45ODcxIDEwMi45NTkgNjcuNTE0M0MxMDMuNzU2IDcwLjk5OTEgMTAzLjk1NiA3NC40Nzc4IDEwMy42MjcgNzcuODM5N0w4OC4xMTY2IDczLjM1MTVMODguMTAzNSA3My4yODU3Qzg2LjcxNDUgNzIuOTA0MyA4NS44NjA5IDcxLjQ4NDggODYuMTg0MyA3MC4wNjExQzg2LjMxNjggNjkuNDc3OCA4Ni42MjQ5IDY4Ljk4NDQgODcuMDQyMyA2OC42MTk4TDg3LjAzNTggNjguNTg2OUw5OS4wMDk0IDU3LjgyMDZaTTY5LjUyNzQgNjkuNDY4OEg3NC40NTk2TDc3LjUyNTEgNzMuMzE4Nkw3Ni40MjQ3IDc4LjEyMjZMNzEuOTk2OCA4MC4yNjE0TDY3LjU1NTggNzguMTE2MUw2Ni40NTU0IDczLjMxMkw2OS41Mjc0IDY5LjQ2ODhaTTg1LjMzOTMgODIuNjQzN0M4NS41NDg5IDgyLjYzMzEgODUuNzU3NiA4Mi42NTIgODUuOTYxNiA4Mi42ODk4TDg1Ljk4NzggODIuNjU2OUwxMDEuOTUgODUuMzY4MkM5OS42MTQyIDkxLjk2MjQgOTUuMTQ0IDk3LjY3NSA4OS4xNzExIDEwMS40OThMODIuOTc0NyA4Ni40NjA2TDgyLjk5NDQgODYuNDM0M0M4Mi40MjUyIDg1LjEwNTUgODIuOTk0OCA4My41NDcyIDg0LjMwNDQgODIuOTEzNUM4NC42Mzk3IDgyLjc1MTMgODQuOTkgODIuNjYxNCA4NS4zMzkzIDgyLjY0MzdaTTU4LjUyOTggODIuNzA5NUM1OS43NDggODIuNzI2NyA2MC44NDA2IDgzLjU3NjEgNjEuMTIzNyA4NC44MjJDNjEuMjU2MiA4NS40MDUyIDYxLjE5MTcgODUuOTgzMSA2MC45NzMgODYuNDkzNUw2MS4wMTg5IDg2LjU1MjdMNTQuODg4IDEwMS40MzlDNDkuMTU1OSA5Ny43NDMyIDQ0LjU5MDQgOTIuMjA5OSA0Mi4xNDgxIDg1LjQyMDhMNTcuOTczMSA4Mi43MjI3TDU3Ljk5OTMgODIuNzU1NkM1OC4xNzYzIDgyLjcyMjkgNTguMzU1OCA4Mi43MDcxIDU4LjUyOTggODIuNzA5NVpNNzEuODk4NiA4OS4yMzEyQzcyLjMyMjkgODkuMjE1NSA3Mi43NTM0IDg5LjMwMyA3My4xNjI3IDg5LjUwMUM3My42OTkyIDg5Ljc2MDYgNzQuMTEzNiA5MC4xNjkyIDc0LjM3NDUgOTAuNjU5Mkg3NC40MzM0TDgyLjIzNDYgMTA0LjgyMUM4MS4yMjIxIDEwNS4xNjIgODAuMTgxMyAxMDUuNDU0IDc5LjExNjcgMTA1LjY5N0M3My4xNTA1IDEwNy4wNTggNjcuMjAzMiAxMDYuNjQ1IDYxLjgxOCAxMDQuODAyTDY5LjU5OTUgOTAuNjY1OEg2OS42MTI2QzcwLjA3OTUgODkuNzg4OCA3MC45NjUgODkuMjY1NiA3MS44OTg2IDg5LjIzMTJaIiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjI1Ii8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfNjgxXzI4NDUiIHgxPSIxMCIgeTE9IjE1LjUiIHgyPSIxNDQiIHkyPSIxMzEuNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjNEQ4N0ZGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzA1NDdEMCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "storageClass"], ["spec", "nodeGroups"], ["spec", "nodeGroups", "md0"], ["spec", "nodeGroups", "md0", "minReplicas"], ["spec", "nodeGroups", "md0", "maxReplicas"], ["spec", "nodeGroups", "md0", "instanceType"], ["spec", "nodeGroups", "md0", "ephemeralStorage"], ["spec", "nodeGroups", "md0", "roles"], ["spec", "nodeGroups", "md0", "resources"], ["spec", "nodeGroups", "md0", "gpus"], ["spec", "version"], ["spec", "host"], ["spec", "addons"], ["spec", "addons", "certManager"], ["spec", "addons", "certManager", "enabled"], ["spec", "addons", "certManager", "valuesOverride"], ["spec", "addons", "cilium"], ["spec", "addons", "cilium", "valuesOverride"], ["spec", "addons", "gatewayAPI"], ["spec", "addons", "gatewayAPI", "enabled"], ["spec", "addons", "ingressNginx"], ["spec", "addons", "ingressNginx", "enabled"], ["spec", "addons", "ingressNginx", "exposeMethod"], ["spec", "addons", "ingressNginx", "hosts"], ["spec", "addons", "ingressNginx", "valuesOverride"], ["spec", "addons", "gpuOperator"], ["spec", "addons", "gpuOperator", "enabled"], ["spec", "addons", "gpuOperator", "valuesOverride"], ["spec", "addons", "fluxcd"], ["spec", "addons", "fluxcd", "enabled"], ["spec", "addons", "fluxcd", "valuesOverride"], ["spec", "addons", "monitoringAgents"], ["spec", "addons", "monitoringAgents", "enabled"], ["spec", "addons", "monitoringAgents", "valuesOverride"], ["spec", "addons", "verticalPodAutoscaler"], ["spec", "addons", "verticalPodAutoscaler", "valuesOverride"], ["spec", "addons", "velero"], ["spec", "addons", "velero", "enabled"], ["spec", "addons", "velero", "valuesOverride"], ["spec", "addons", "coredns"], ["spec", "addons", "coredns", "valuesOverride"], ["spec", "controlPlane"], ["spec", "controlPlane", "replicas"], ["spec", "controlPlane", "apiServer"], ["spec", "controlPlane", "apiServer", "resources"], ["spec", "controlPlane", "apiServer", "resourcesPreset"], ["spec", "controlPlane", "controllerManager"], ["spec", "controlPlane", "controllerManager", "resources"], ["spec", "controlPlane", "controllerManager", "resourcesPreset"], ["spec", "controlPlane", "scheduler"], ["spec", "controlPlane", "scheduler", "resources"], ["spec", "controlPlane", "scheduler", "resourcesPreset"], ["spec", "controlPlane", "konnectivity"], ["spec", "controlPlane", "konnectivity", "server"], ["spec", "controlPlane", "konnectivity", "server", "resources"], ["spec", "controlPlane", "konnectivity", "server", "resourcesPreset"]] + secrets: + exclude: [] + include: + - resourceNames: + - kubernetes-{{ .name }}-admin-kubeconfig + services: + exclude: [] + include: + - resourceNames: + - kubernetes-{{ .name }} + ingresses: + exclude: [] + include: + - resourceNames: + - kubernetes-{{ .name }} diff --git a/packages/system/kubernetes-rd/templates/cozyrd.yaml b/packages/system/kubernetes-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/kubernetes-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/kubernetes-rd/values.yaml b/packages/system/kubernetes-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/kubernetes-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/monitoring-rd/Chart.yaml b/packages/system/monitoring-rd/Chart.yaml new file mode 100644 index 00000000..cac8a99d --- /dev/null +++ b/packages/system/monitoring-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: monitoring-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/monitoring-rd/Makefile b/packages/system/monitoring-rd/Makefile new file mode 100644 index 00000000..ecce61e3 --- /dev/null +++ b/packages/system/monitoring-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=monitoring-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/monitoring-rd/cozyrds/monitoring.yaml b/packages/system/monitoring-rd/cozyrds/monitoring.yaml new file mode 100644 index 00000000..720dc161 --- /dev/null +++ b/packages/system/monitoring-rd/cozyrds/monitoring.yaml @@ -0,0 +1,48 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: monitoring +spec: + application: + kind: Monitoring + singular: monitoring + plural: monitorings + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"alerta":{"description":"Configuration for the Alerta service.","type":"object","default":{},"properties":{"alerts":{"description":"Alert routing configuration.","type":"object","default":{},"properties":{"slack":{"description":"Configuration for Slack alerts.","type":"object","default":{},"required":["url"],"properties":{"url":{"description":"Configuration uri for Slack alerts.","type":"string","default":""}}},"telegram":{"description":"Configuration for Telegram alerts.","type":"object","default":{},"required":["chatID","token"],"properties":{"chatID":{"description":"Telegram chat ID(s), separated by commas.","type":"string","default":""},"disabledSeverity":{"description":"List of severities without alerts (e.g. \"informational,warning\").","type":"string","default":""},"token":{"description":"Telegram bot token.","type":"string","default":""}}}}},"resources":{"description":"Resource configuration.","type":"object","default":{},"properties":{"limits":{"description":"Resource limits.","type":"object","default":{},"properties":{"cpu":{"description":"CPU limit.","default":1,"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory limit.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"requests":{"description":"Resource requests.","type":"object","default":{},"properties":{"cpu":{"description":"CPU request.","default":"100m","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory request.","default":"256Mi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}},"storage":{"description":"Persistent volume size for the database.","type":"string","default":"10Gi"},"storageClassName":{"description":"StorageClass used for the database.","type":"string","default":""}}},"grafana":{"description":"Configuration for Grafana.","type":"object","default":{},"properties":{"db":{"description":"Database configuration.","type":"object","default":{},"properties":{"size":{"description":"Persistent volume size for the database.","type":"string","default":"10Gi"}}},"resources":{"description":"Resource configuration.","type":"object","default":{},"properties":{"limits":{"description":"Resource limits.","type":"object","default":{},"properties":{"cpu":{"description":"CPU limit.","default":1,"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory limit.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"requests":{"description":"Resource requests.","type":"object","default":{},"properties":{"cpu":{"description":"CPU request.","default":"100m","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory request.","default":"256Mi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}}}},"host":{"description":"The hostname used to access Grafana externally (defaults to 'grafana' subdomain for the tenant host).","type":"string","default":""},"logsStorages":{"description":"Configuration of logs storage instances.","type":"array","default":[{"name":"generic","retentionPeriod":"1","storage":"10Gi","storageClassName":"replicated"}],"items":{"type":"object","required":["name","retentionPeriod","storage","storageClassName"],"properties":{"name":{"description":"Name of the storage instance.","type":"string"},"retentionPeriod":{"description":"Retention period for logs.","type":"string","default":"1"},"storage":{"description":"Persistent volume size.","type":"string","default":"10Gi"},"storageClassName":{"description":"StorageClass used to store the data.","type":"string","default":"replicated"}}}},"metricsStorages":{"description":"Configuration of metrics storage instances.","type":"array","default":[{"deduplicationInterval":"15s","name":"shortterm","retentionPeriod":"3d","storage":"10Gi","storageClassName":""},{"deduplicationInterval":"5m","name":"longterm","retentionPeriod":"14d","storage":"10Gi","storageClassName":""}],"items":{"type":"object","required":["deduplicationInterval","name","retentionPeriod","storage"],"properties":{"deduplicationInterval":{"description":"Deduplication interval for metrics.","type":"string"},"name":{"description":"Name of the storage instance.","type":"string"},"retentionPeriod":{"description":"Retention period for metrics.","type":"string"},"storage":{"description":"Persistent volume size.","type":"string","default":"10Gi"},"storageClassName":{"description":"StorageClass used for the data.","type":"string"},"vminsert":{"description":"Configuration for vminsert.","type":"object","properties":{"maxAllowed":{"description":"Maximum allowed resources.","type":"object","properties":{"cpu":{"description":"CPU limit.","default":1,"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory limit.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"minAllowed":{"description":"Minimum guaranteed resources.","type":"object","properties":{"cpu":{"description":"CPU request.","default":"100m","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory request.","default":"256Mi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}},"vmselect":{"description":"Configuration for vmselect.","type":"object","properties":{"maxAllowed":{"description":"Maximum allowed resources.","type":"object","properties":{"cpu":{"description":"CPU limit.","default":1,"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory limit.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"minAllowed":{"description":"Minimum guaranteed resources.","type":"object","properties":{"cpu":{"description":"CPU request.","default":"100m","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory request.","default":"256Mi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}},"vmstorage":{"description":"Configuration for vmstorage.","type":"object","properties":{"maxAllowed":{"description":"Maximum allowed resources.","type":"object","properties":{"cpu":{"description":"CPU limit.","default":1,"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory limit.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"minAllowed":{"description":"Minimum guaranteed resources.","type":"object","properties":{"cpu":{"description":"CPU request.","default":"100m","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory request.","default":"256Mi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}}}}},"vmagent":{"description":"Configuration for VictoriaMetrics Agent.","type":"object","default":{},"properties":{"externalLabels":{"description":"External labels applied to all metrics.","default":{"cluster":"cozystack"}},"remoteWrite":{"description":"Remote write configuration.","default":{"urls":["http://vminsert-shortterm:8480/insert/0/prometheus","http://vminsert-longterm:8480/insert/0/prometheus"]}}}}}} + release: + prefix: "" + labels: + cozystack.io/ui: "true" + internal.cozystack.io/tenantmodule: "true" + chart: + name: monitoring + sourceRef: + kind: HelmRepository + name: cozystack-extra + namespace: cozy-public + dashboard: + category: Administration + singular: Monitoring + plural: Monitoring + name: monitoring + description: Monitoring and observability stack + module: true + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODdfMzI2OCkiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzY4N18zMjY4KSI+CjxwYXRoIGQ9Ik04OS41MDM5IDExMS43MDdINTQuNDk3QzU0LjE3MjcgMTExLjcwNyA1NC4wMTA4IDExMS4yMjEgNTQuMzM0OSAxMTEuMDU5TDU3LjI1MjIgMTA4Ljk1MkM2MC4zMzE0IDEwNi42ODMgNjEuOTUyMiAxMDIuNjMxIDYwLjk3OTcgOTguNzQxMkg4My4wMjFDODIuMDQ4NSAxMDIuNjMxIDgzLjY2OTMgMTA2LjY4MyA4Ni43NDg1IDEwOC45NTJMODkuNjY1OCAxMTEuMDU5Qzg5Ljk5IDExMS4yMjEgODkuODI3OSAxMTEuNzA3IDg5LjUwMzkgMTExLjcwN1oiIGZpbGw9IiNCMEI2QkIiLz4KPHBhdGggZD0iTTExMy4zMjggOTguNzQxSDMwLjY3MjVDMjcuNTkzMSA5OC43NDEgMjUgOTYuMTQ4IDI1IDkzLjA2ODdWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJWOTMuMDY4N0MxMTkgOTYuMTQ4IDExNi40MDcgOTguNzQxIDExMy4zMjggOTguNzQxWiIgZmlsbD0iI0U4RURFRSIvPgo8cGF0aCBkPSJNMTE5IDg0LjE1NDlIMjVWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJMMTE5IDg0LjE1NDlaIiBmaWxsPSIjMzg0NTRGIi8+CjxwYXRoIGQ9Ik05MC42Mzc0IDExNi41NjlINTMuMzYxNkM1Mi4wNjUxIDExNi41NjkgNTAuOTMwNyAxMTUuNDM1IDUwLjkzMDcgMTE0LjEzOEM1MC45MzA3IDExMi44NDEgNTIuMDY1MSAxMTEuNzA3IDUzLjM2MTYgMTExLjcwN0g5MC42Mzc0QzkxLjkzMzkgMTExLjcwNyA5My4wNjg0IDExMi44NDEgOTMuMDY4NCAxMTQuMTM4QzkzLjA2ODQgMTE1LjQzNSA5MS45MzM5IDExNi41NjkgOTAuNjM3NCAxMTYuNTY5WiIgZmlsbD0iI0U4RURFRSIvPgo8cGF0aCBkPSJNNTQuMTcyMiAzOC43NzU3SDMzLjEwMzJDMzIuMTMwNyAzOC43NzU3IDMxLjQ4MjQgMzguMTI3NCAzMS40ODI0IDM3LjE1NDlDMzEuNDgyNCAzNi4xODI0IDMyLjEzMDcgMzUuNTM0MiAzMy4xMDMyIDM1LjUzNDJINTQuMTcyMkM1NS4xNDQ3IDM1LjUzNDIgNTUuNzkzIDM2LjE4MjQgNTUuNzkzIDM3LjE1NDlDNTUuNzkyOCAzOC4xMjc0IDU1LjE0NDUgMzguNzc1NyA1NC4xNzIyIDM4Ljc3NTdaIiBmaWxsPSIjREQzNDJFIi8+CjxwYXRoIGQ9Ik02My44OTYzIDQ1LjI1OTFINDEuMjA2N0M0MC4yMzQyIDQ1LjI1OTEgMzkuNTg1OSA0NC42MTA4IDM5LjU4NTkgNDMuNjM4M0MzOS41ODU5IDQyLjY2NTggNDAuMjM0MiA0Mi4wMTc2IDQxLjIwNjcgNDIuMDE3Nkg2My44OTYzQzY0Ljg2ODggNDIuMDE3NiA2NS41MTcxIDQyLjY2NTggNjUuNTE3MSA0My42MzgzQzY1LjUxNzEgNDQuNjEwOCA2NC44Njg4IDQ1LjI1OTEgNjMuODk2MyA0NS4yNTkxWiIgZmlsbD0iIzczODNCRiIvPgo8cGF0aCBkPSJNMzQuNzI0IDQ1LjI1OTFIMzMuMTAzMkMzMi4xMzA3IDQ1LjI1OTEgMzEuNDgyNCA0NC42MTA4IDMxLjQ4MjQgNDMuNjM4M0MzMS40ODI0IDQyLjY2NTggMzIuMTMwNyA0Mi4wMTc2IDMzLjEwMzIgNDIuMDE3NkgzNC43MjRDMzUuNjk2NCA0Mi4wMTc2IDM2LjM0NDcgNDIuNjY1OCAzNi4zNDQ3IDQzLjYzODNDMzYuMzQ0NyA0NC42MTA4IDM1LjY5NjMgNDUuMjU5MSAzNC43MjQgNDUuMjU5MVoiIGZpbGw9IiM0MkIwNUMiLz4KPHBhdGggZD0iTTYzLjg5NjMgMzguNzc1N0g2MC42NTQ5QzU5LjY4MjQgMzguNzc1NyA1OS4wMzQyIDM4LjEyNzQgNTkuMDM0MiAzNy4xNTQ5QzU5LjAzNDIgMzYuMTgyNCA1OS42ODI0IDM1LjUzNDIgNjAuNjU0OSAzNS41MzQySDYzLjg5NjNDNjQuODY4OCAzNS41MzQyIDY1LjUxNzEgMzYuMTgyNCA2NS41MTcxIDM3LjE1NDlDNjUuNTE3MSAzOC4xMjc0IDY0Ljg2ODggMzguNzc1NyA2My44OTYzIDM4Ljc3NTdaIiBmaWxsPSIjRUNCQTE2Ii8+CjxwYXRoIGQ9Ik00Ny42ODkzIDUxLjc0MTNIMzMuMTAzMkMzMi4xMzA3IDUxLjc0MTMgMzEuNDgyNCA1MS4wOTMxIDMxLjQ4MjQgNTAuMTIwNkMzMS40ODI0IDQ5LjE0ODEgMzIuMTMwNyA0OC41IDMzLjEwMzIgNDguNUg0Ny42ODkzQzQ4LjY2MTggNDguNSA0OS4zMTAxIDQ5LjE0ODMgNDkuMzEwMSA1MC4xMjA4QzQ5LjMxMDEgNTEuMDkzMyA0OC42NjE4IDUxLjc0MTMgNDcuNjg5MyA1MS43NDEzWiIgZmlsbD0iI0REMzQyRSIvPgo8cGF0aCBkPSJNNjMuODk2OCA1MS43NDEzSDU0LjE3MjVDNTMuMiA1MS43NDEzIDUyLjU1MTggNTEuMDkzMSA1Mi41NTE4IDUwLjEyMDZDNTIuNTUxOCA0OS4xNDgxIDUzLjIwMDIgNDguNSA1NC4xNzI3IDQ4LjVINjMuODk2OUM2NC44Njk0IDQ4LjUgNjUuNTE3NyA0OS4xNDgzIDY1LjUxNzcgNTAuMTIwOEM2NS41MTc3IDUxLjA5MzMgNjQuODY5MiA1MS43NDEzIDYzLjg5NjggNTEuNzQxM1oiIGZpbGw9IiNFQ0JBMTYiLz4KPHBhdGggZD0iTTU0LjE3MjIgNTguMjI0SDMzLjEwMzJDMzIuMTMwNyA1OC4yMjQgMzEuNDgyNCA1Ny41NzU3IDMxLjQ4MjQgNTYuNjAzMkMzMS40ODI0IDU1LjYzMDcgMzIuMTMwNyA1NC45ODI0IDMzLjEwMzIgNTQuOTgyNEg1NC4xNzIyQzU1LjE0NDcgNTQuOTgyNCA1NS43OTMgNTUuNjMwNyA1NS43OTMgNTYuNjAzMkM1NS43OTMgNTcuNTc1NyA1NS4xNDQ1IDU4LjIyNCA1NC4xNzIyIDU4LjIyNFoiIGZpbGw9IiM0MkIwNUMiLz4KPHBhdGggZD0iTTYzLjg5NjMgNjQuNzA3NEg0MS4yMDY3QzQwLjIzNDIgNjQuNzA3NCAzOS41ODU5IDY0LjA1OTEgMzkuNTg1OSA2My4wODY2QzM5LjU4NTkgNjIuMTE0MSA0MC4yMzQyIDYxLjQ2NTggNDEuMjA2NyA2MS40NjU4SDYzLjg5NjNDNjQuODY4OCA2MS40NjU4IDY1LjUxNzEgNjIuMTE0MSA2NS41MTcxIDYzLjA4NjZDNjUuNTE3MSA2NC4wNTkxIDY0Ljg2ODggNjQuNzA3NCA2My44OTYzIDY0LjcwNzRaIiBmaWxsPSIjRUNCQTE2Ii8+CjxwYXRoIGQ9Ik0zNC43MjQgNjQuNzA3NEgzMy4xMDMyQzMyLjEzMDcgNjQuNzA3NCAzMS40ODI0IDY0LjA1OTEgMzEuNDgyNCA2My4wODY2QzMxLjQ4MjQgNjIuMTE0MSAzMi4xMzA3IDYxLjQ2NTggMzMuMTAzMiA2MS40NjU4SDM0LjcyNEMzNS42OTY0IDYxLjQ2NTggMzYuMzQ0NyA2Mi4xMTQxIDM2LjM0NDcgNjMuMDg2NkMzNi4zNDQ3IDY0LjA1OTEgMzUuNjk2MyA2NC43MDc0IDM0LjcyNCA2NC43MDc0WiIgZmlsbD0iI0REMzQyRSIvPgo8cGF0aCBkPSJNNDcuNjg5MyA3MS4xODk4SDMzLjEwMzJDMzIuMTMwNyA3MS4xODk4IDMxLjQ4MjQgNzAuNTQxNSAzMS40ODI0IDY5LjU2OUMzMS40ODI0IDY4LjU5NjUgMzIuMTMwNyA2Ny45NDgyIDMzLjEwMzIgNjcuOTQ4Mkg0Ny42ODkzQzQ4LjY2MTggNjcuOTQ4MiA0OS4zMTAxIDY4LjU5NjUgNDkuMzEwMSA2OS41NjlDNDkuMzEwMSA3MC41NDE1IDQ4LjY2MTggNzEuMTg5OCA0Ny42ODkzIDcxLjE4OThaIiBmaWxsPSIjNDJCMDVDIi8+CjxwYXRoIGQ9Ik02My44OTY4IDcxLjE4OThINTQuMTcyNUM1My4yIDcxLjE4OTggNTIuNTUxOCA3MC41NDE1IDUyLjU1MTggNjkuNTY5QzUyLjU1MTggNjguNTk2NSA1My4yIDY3Ljk0ODIgNTQuMTcyNSA2Ny45NDgySDYzLjg5NjhDNjQuODY5MiA2Ny45NDgyIDY1LjUxNzUgNjguNTk2NSA2NS41MTc1IDY5LjU2OUM2NS41MTc1IDcwLjU0MTUgNjQuODY5MiA3MS4xODk4IDYzLjg5NjggNzEuMTg5OFoiIGZpbGw9IiM3MzgzQkYiLz4KPHBhdGggZD0iTTU0LjE3MjIgNzcuNjcyMkgzMy4xMDMyQzMyLjEzMDcgNzcuNjcyMiAzMS40ODI0IDc3LjAyMzkgMzEuNDgyNCA3Ni4wNTE0QzMxLjQ4MjQgNzUuMDc4OSAzMi4xMzA3IDc0LjQzMDcgMzMuMTAzMiA3NC40MzA3SDU0LjE3MjJDNTUuMTQ0NyA3NC40MzA3IDU1Ljc5MyA3NS4wNzg5IDU1Ljc5MyA3Ni4wNTE0QzU1Ljc5MjggNzcuMDIzOSA1NS4xNDQ1IDc3LjY3MjIgNTQuMTcyMiA3Ny42NzIyWiIgZmlsbD0iI0VDQkExNiIvPgo8cGF0aCBkPSJNNjMuODk2MyA3Ny42NzIySDYwLjY1NDlDNTkuNjgyNCA3Ny42NzIyIDU5LjAzNDIgNzcuMDIzOSA1OS4wMzQyIDc2LjA1MTRDNTkuMDM0MiA3NS4wNzg5IDU5LjY4MjQgNzQuNDMwNyA2MC42NTQ5IDc0LjQzMDdINjMuODk2M0M2NC44Njg4IDc0LjQzMDcgNjUuNTE3MSA3NS4wNzg5IDY1LjUxNzEgNzYuMDUxNEM2NS41MTcxIDc3LjAyMzkgNjQuODY4OCA3Ny42NzIyIDYzLjg5NjMgNzcuNjcyMloiIGZpbGw9IiM0MkIwNUMiLz4KPHBhdGggZD0iTTEwMS4xNzIgNzcuNjcyMkg4MC4xMDMyQzc5LjEzMDcgNzcuNjcyMiA3OC40ODI0IDc3LjAyMzkgNzguNDgyNCA3Ni4wNTE0Qzc4LjQ4MjQgNzUuMDc4OSA3OS4xMzA3IDc0LjQzMDcgODAuMTAzMiA3NC40MzA3SDEwMS4xNzJDMTAyLjE0NSA3NC40MzA3IDEwMi43OTMgNzUuMDc4OSAxMDIuNzkzIDc2LjA1MTRDMTAyLjc5MyA3Ny4wMjM5IDEwMi4xNDUgNzcuNjcyMiAxMDEuMTcyIDc3LjY3MjJaIiBmaWxsPSIjREQzNDJFIi8+CjxwYXRoIGQ9Ik0xMTAuODk2IDc3LjY3MjJIMTA3LjY1NUMxMDYuNjgyIDc3LjY3MjIgMTA2LjAzNCA3Ny4wMjM5IDEwNi4wMzQgNzYuMDUxNEMxMDYuMDM0IDc1LjA3ODkgMTA2LjY4MiA3NC40MzA3IDEwNy42NTUgNzQuNDMwN0gxMTAuODk2QzExMS44NjkgNzQuNDMwNyAxMTIuNTE3IDc1LjA3ODkgMTEyLjUxNyA3Ni4wNTE0QzExMi41MTcgNzcuMDIzOSAxMTEuODY5IDc3LjY3MjIgMTEwLjg5NiA3Ny42NzIyWiIgZmlsbD0iIzQyQjA1QyIvPgo8cGF0aCBkPSJNNjMuODk2MyA1OC4yMjRINjAuNjU0OUM1OS42ODI0IDU4LjIyNCA1OS4wMzQyIDU3LjU3NTcgNTkuMDM0MiA1Ni42MDMyQzU5LjAzNDIgNTUuNjMwNyA1OS42ODI0IDU0Ljk4MjQgNjAuNjU0OSA1NC45ODI0SDYzLjg5NjNDNjQuODY4OCA1NC45ODI0IDY1LjUxNzEgNTUuNjMwNyA2NS41MTcxIDU2LjYwMzJDNjUuNTE3MSA1Ny41NzU3IDY0Ljg2ODggNTguMjI0IDYzLjg5NjMgNTguMjI0WiIgZmlsbD0iIzczODNCRiIvPgo8cGF0aCBkPSJNMTEyLjUxNyA1MS43NDExQzExMi41MTcgNjAuNjU0OSAxMDUuMjI0IDY3Ljk0OCA5Ni4zMTA0IDY3Ljk0OEM4Ny4zOTY2IDY3Ljk0OCA4MC4xMDM1IDYwLjY1NDkgODAuMTAzNSA1MS43NDExQzgwLjEwMzUgNDIuODI3MyA4Ny4zOTY2IDM1LjUzNDIgOTYuMzEwNCAzNS41MzQyQzEwNS4yMjQgMzUuNTM0MiAxMTIuNTE3IDQyLjgyNzMgMTEyLjUxNyA1MS43NDExWiIgZmlsbD0iI0VDQkExNiIvPgo8cGF0aCBkPSJNODAuMTAzNSA1MS43NDExQzgwLjEwMzUgNTIuMjI3MyA4MC4xMDM1IDUyLjg3NTUgODAuMTAzNSA1My4zNjE5SDk2LjMxMDRWMzUuNTM0MkM4Ny4zOTY2IDM1LjUzNDIgODAuMTAzNSA0Mi44MjczIDgwLjEwMzUgNTEuNzQxMVoiIGZpbGw9IiM0MkIwNUMiLz4KPC9nPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4N18zMjY4IiB4MT0iMS4yMzIzOWUtMDYiIHkxPSItOS41MDAwMSIgeDI9IjE2OCIgeTI9IjE2MiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjOEZEREZGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwNzVGRiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzY4N18zMjY4Ij4KPHJlY3Qgd2lkdGg9Ijk0IiBoZWlnaHQ9Ijk0IiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjUgMjUpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg== + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "host"], ["spec", "metricsStorages"], ["spec", "logsStorages"], ["spec", "alerta"], ["spec", "alerta", "storage"], ["spec", "alerta", "storageClassName"], ["spec", "alerta", "resources"], ["spec", "alerta", "resources", "limits"], ["spec", "alerta", "resources", "limits", "cpu"], ["spec", "alerta", "resources", "limits", "memory"], ["spec", "alerta", "resources", "requests"], ["spec", "alerta", "resources", "requests", "cpu"], ["spec", "alerta", "resources", "requests", "memory"], ["spec", "alerta", "alerts"], ["spec", "alerta", "alerts", "telegram"], ["spec", "alerta", "alerts", "telegram", "token"], ["spec", "alerta", "alerts", "telegram", "chatID"], ["spec", "alerta", "alerts", "telegram", "disabledSeverity"], ["spec", "alerta", "alerts", "slack"], ["spec", "alerta", "alerts", "slack", "url"], ["spec", "grafana"], ["spec", "grafana", "db"], ["spec", "grafana", "db", "size"], ["spec", "grafana", "resources"], ["spec", "grafana", "resources", "limits"], ["spec", "grafana", "resources", "limits", "cpu"], ["spec", "grafana", "resources", "limits", "memory"], ["spec", "grafana", "resources", "requests"], ["spec", "grafana", "resources", "requests", "cpu"], ["spec", "grafana", "resources", "requests", "memory"], ["spec", "vmagent"], ["spec", "vmagent", "externalLabels"], ["spec", "vmagent", "externalLabels", "cluster"], ["spec", "vmagent", "remoteWrite"], ["spec", "vmagent", "remoteWrite", "urls"]] + secrets: + exclude: [] + include: + - resourceNames: + - grafana-admin-password + services: + exclude: [] + include: + - resourceNames: + - grafana-service + - alerta + ingresses: + exclude: [] + include: + - resourceNames: + - grafana-ingress + - alerta diff --git a/packages/system/monitoring-rd/templates/cozyrd.yaml b/packages/system/monitoring-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/monitoring-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/monitoring-rd/values.yaml b/packages/system/monitoring-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/monitoring-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/mysql-rd/Chart.yaml b/packages/system/mysql-rd/Chart.yaml new file mode 100644 index 00000000..d3c6b081 --- /dev/null +++ b/packages/system/mysql-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: mysql-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/mysql-rd/Makefile b/packages/system/mysql-rd/Makefile new file mode 100644 index 00000000..a2d28923 --- /dev/null +++ b/packages/system/mysql-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=mysql-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/mysql-rd/cozyrds/mysql.yaml b/packages/system/mysql-rd/cozyrds/mysql.yaml new file mode 100644 index 00000000..ba8251b8 --- /dev/null +++ b/packages/system/mysql-rd/cozyrds/mysql.yaml @@ -0,0 +1,41 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: mysql +spec: + application: + kind: MySQL + plural: mysqls + singular: mysql + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["cleanupStrategy","enabled","resticPassword","s3AccessKey","s3Bucket","s3Region","s3SecretKey","schedule"],"properties":{"cleanupStrategy":{"description":"Retention strategy for cleaning up old backups.","type":"string","default":"--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"},"enabled":{"description":"Enable regular backups (default: false).","type":"boolean","default":false},"resticPassword":{"description":"Password for Restic backup encryption.","type":"string","default":""},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3Bucket":{"description":"S3 bucket used for storing backups.","type":"string","default":"s3.example.org/mysql-backups"},"s3Region":{"description":"AWS S3 region where backups are stored.","type":"string","default":"us-east-1"},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * *"}}},"databases":{"description":"Databases configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"roles":{"description":"Roles assigned to users.","type":"object","properties":{"admin":{"description":"List of users with admin privileges.","type":"array","items":{"type":"string"}},"readonly":{"description":"List of users with read-only privileges.","type":"array","items":{"type":"string"}}}}}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"replicas":{"description":"Number of MariaDB replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each MariaDB replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","required":["maxUserConnections","password"],"properties":{"maxUserConnections":{"description":"Maximum number of connections.","type":"integer"},"password":{"description":"Password for the user.","type":"string"}}}},"version":{"description":"MariaDB major.minor version to deploy","type":"string","default":"v11.8","enum":["v11.8","v11.4","v10.11","v10.6"]}}} + release: + prefix: mysql- + labels: + cozystack.io/ui: "true" + chart: + name: mysql + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: MySQL + plural: MySQL + description: Managed MariaDB service + tags: + - database + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9Ii0wLjAwMTk1MzEyIiB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgcng9IjI0IiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfNjgzXzI5MzApIi8+CjxwYXRoIGQ9Ik0xMzMuMTkxIDI5LjAwMjJDMTMxLjIxMyAyOS4wNjU0IDEzMS44MzkgMjkuNjM1NCAxMjcuNTY0IDMwLjY4NzNDMTIzLjI0OCAzMS43NDk2IDExNy45NzUgMzEuNDIzOSAxMTMuMzI3IDMzLjM3MzNDOTkuNDUxNiAzOS4xOTI0IDk2LjY2NzYgNTkuMDgxMyA4NC4wNTM1IDY2LjIwNTlDNzQuNjI0NyA3MS41MzE4IDY1LjExMiA3MS45NTY1IDU2LjU1OTQgNzQuNjM2NUM1MC45Mzg5IDc2LjM5OSA0NC43OTA2IDgwLjAxMzUgMzkuNjk4MiA4NC40MDE4QzM1Ljc0NTUgODcuODA5MyAzNS42NDIzIDkwLjgwNTQgMzEuNTEyMyA5NS4wNzkxQzI3LjA5NDcgOTkuNjUwNCAxMy45NTUxIDk1LjE1NjQgOCAxMDIuMTUzQzkuOTE4MzUgMTA0LjA5MyAxMC43NTk0IDEwNC42MzYgMTQuNTM5OCAxMDQuMTMzQzEzLjc1NzEgMTA1LjYxNiA5LjE0MzMyIDEwNi44NjYgMTAuMDQ2NSAxMDkuMDQ5QzEwLjk5NjggMTExLjM0NSAyMi4xNTExIDExMi45MDEgMzIuMjkwOCAxMDYuNzhDMzcuMDEzMSAxMDMuOTI5IDQwLjc3NDMgOTkuODE5MyA0OC4xMjg4IDk4LjgzODRDNTcuNjQ1OSA5Ny41Njk5IDY4LjYwOTMgOTkuNjUyIDc5LjYyNjggMTAxLjI0MUM3Ny45OTMyIDEwNi4xMTIgNzQuNzEzMyAxMDkuMzUxIDcyLjA4NiAxMTMuMjMxQzcxLjI3MjQgMTE0LjEwNyA3My43MjAyIDExNC4yMDUgNzYuNTEyNiAxMTMuNjc1QzgxLjUzNTkgMTEyLjQzMyA4NS4xNTYxIDExMS40MzMgODguOTQ3MiAxMDkuMjI3QzkzLjYwNDcgMTA2LjUxNSA5NC4zMTA0IDk5LjU2MzkgMTAwLjAyNSA5OC4wNTk5QzEwMy4yMDkgMTAyLjk1NCAxMTEuODY5IDEwNC4xMSAxMTcuMjQyIDEwMC4xOTVDMTEyLjUyNyA5OC44NjA3IDExMS4yMjQgODguODI0NCAxMTIuODE1IDg0LjQwMThDMTE0LjMyMyA4MC4yMTU2IDExNS44MTMgNzMuNTE5MiAxMTcuMzMxIDY3Ljk4NTVDMTE4Ljk2MSA2Mi4wNDI1IDExOS41NjIgNTQuNTUxOSAxMjEuNTM1IDUxLjUyNDdDMTI0LjUwMyA0Ni45NzAxIDEyNy43ODMgNDUuNDA2IDEzMC42MyA0Mi44Mzc3QzEzMy40NzcgNDAuMjY5NSAxMzYuMDgzIDM3Ljc2OTQgMTM1Ljk5OCAzMS44OTI3QzEzNS45NyAyOS45OTk4IDEzNC45OTIgMjguOTQ0NyAxMzMuMTkxIDI5LjAwMjJaIiBmaWxsPSIjMDQyNDRFIi8+CjxwYXRoIGQ9Ik0xMjguOTUzIDMyLjQ4NDRDMTI5LjQyNyAzNC4xMDA0IDEzMC4xNjggMzQuODQyMSAxMzMuMzc1IDM1LjEzODdDMTMyLjkwNiAzOS4yMDQxIDEzMC4xOTUgNDEuNDI3NiAxMjcuMTU0IDQzLjU2MTFDMTI0LjQ3OSA0NS40Mzc2IDEyMS41NDcgNDcuMjQ0NSAxMTkuNjY0IDUwLjE3NTdDMTE3LjczNCA1My4xNzg1IDExNi41MDkgNjMuNDU1NCAxMTMuNTE3IDczLjYwNDRDMTEwLjkzMSA4Mi4zNzM4IDEwNy4wMjUgOTEuMDQ0NSAxMDAuMjA0IDk0Ljg0MzdDOTkuNDkxOSA5My4wNTAyIDEwMC4yOTUgODkuNzQgOTguODc4IDg4LjY1MkM5Ny45NjExIDkxLjI2NzQgOTYuOTI0MiA5My43NjI3IDk1LjcwOTggOTYuMDgyMUM5MS43MDc3IDEwMy43MzIgODUuNzgyMiAxMDkuNDU5IDc1Ljg4MDEgMTExLjIwOEM4MC41Nzg1IDEwNC44NSA4NS4wNzEgOTguMjg0NCA4NS4xNjgzIDg3LjMyNjJDODEuODYxNyA4OC4wNDE3IDgxLjkzMTkgOTUuODUyMiA3OC41MzQ1IDk3Ljk0MDJDNzYuMzU2MyA5OC4xNzcyIDc0LjE0OTggOTguMTc1OCA3MS45MjkxIDk4LjA0MjRDNjIuODA5MSA5Ny40OTU5IDUzLjQ1MzUgOTQuNzU0OSA0NC45MjE5IDk3LjQ5MjNDMzkuMTEyOCA5OS4zNTY4IDM0LjM2MTkgMTAzLjc1NSAyOS40NDI4IDEwNS44ODhDMjMuNjYxNCAxMDguMzk2IDE5LjI4MzEgMTA5LjQyNyAxMi4wODM2IDEwOC4zOTZDMTEuMTY5NSAxMDcuMTY0IDE3LjM1MjYgMTA1LjU3NSAxNi45ODI5IDEwMi45MDJDMTQuMTY1MyAxMDIuNTkgMTIuNTI5MyAxMDMuMjczIDEwLjA4MDEgMTAyLjE2QzEwLjM1MDUgMTAxLjY2MiAxMC43NDc5IDEwMS4yNDcgMTEuMjQ4MyAxMDAuOTAxQzE1LjczNzMgOTcuNzk0IDI4LjQ4ODIgMTAwLjE2NyAzMS45MDA2IDk2LjgxNjdDMzQuMDA3IDk0Ljc1IDM1LjM4ODkgOTIuNTg2NyAzNi44MTk3IDkwLjQ4MzhDMzguMjA3MiA4OC40NDM0IDM5LjY0MTUgODYuNDU5NyA0MS44MjY4IDg0LjY3MTlDNDIuNjMzNyA4NC4wMTE4IDQzLjUxMSA4My4zNTk2IDQ0LjQ0MjEgODIuNzIzQzQ4LjE2NiA4MC4xNzQzIDUyLjc3MjkgNzcuODYyOCA1Ny4zMDY2IDc2LjI2OTRDNjMuNDgyNiA3NC4wOTg0IDY5Ljc0MSA3My45MTk1IDc2LjMyMzcgNzEuNDA0M0M4MC4zOTA0IDY5Ljg1IDg0LjgxMjcgNjcuOTMwMiA4OC40MTc0IDY1LjI0MzlDODkuMjczMyA2NC42MDUxIDkwLjA4MzEgNjMuOTI0NSA5MC44MzE5IDYzLjE5NDlDMTAxLjEyNSA1My4xNjA4IDEwMy4xNjUgMzUuNDYxIDExOS4yMjQgMzMuODExNkMxMjEuMTY2IDMzLjYxMjEgMTIyLjc1NiAzMy42NzY3IDEyNC4yMDMgMzMuNjMyN0MxMjUuODcxIDMzLjU4MyAxMjcuMzQ3IDMzLjM4OTMgMTI4Ljk1MyAzMi40ODQ0Wk0xMDkuMzc1IDg5LjEzMzlDMTA5LjU2NyA5Mi4yMDEzIDExMS4zNDggOTguMjg3MiAxMTIuOTIgOTkuNzY2M0MxMDkuODQxIDEwMC41MTUgMTA0LjUzNyA5OS4yNzggMTAzLjE3NyA5Ny4xMDYyQzEwMy44NzYgOTMuOTcwNyAxMDcuNTE0IDkxLjEwNDEgMTA5LjM3NSA4OS4xMzM5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTEzMC4xMDkgMzUuOTE4N0MxMjkuNDkgMzcuMjE2OSAxMjguMzA1IDM4Ljg5MDggMTI4LjMwNSA0Mi4xOTU2QzEyOC4zIDQyLjc2MyAxMjcuODc1IDQzLjE1MTcgMTI3Ljg2NyA0Mi4yNzdDMTI3Ljg5OSAzOS4wNDcgMTI4Ljc1NCAzNy42NTA3IDEyOS42NjIgMzUuODE1NUMxMzAuMDg1IDM1LjA2MzYgMTMwLjMzOSAzNS4zNzM4IDEzMC4xMDkgMzUuOTE4N1pNMTI5LjQ4NiAzNS40Mjk3QzEyOC43NTYgMzYuNjY4NCAxMjYuOTk4IDM4LjkyNzggMTI2LjcwNyA0Mi4yMjAzQzEyNi42NTMgNDIuNzg0OCAxMjYuMTk0IDQzLjEzNDMgMTI2LjI2NCA0Mi4yNjE4QzEyNi41ODEgMzkuMDQ3NyAxMjcuOTg2IDM3LjAzNiAxMjkuMDUyIDM1LjI4NzNDMTI5LjUzNiAzNC41NzYxIDEyOS43NjMgMzQuOTA3NCAxMjkuNDg2IDM1LjQyOTdaTTEyOC45MTggMzQuNzgxN0MxMjguMDg2IDM1Ljk1NDMgMTI1LjM4IDM4LjY2NzggMTI0LjgxNCA0MS45MjQ3QzEyNC43MTIgNDIuNDgxOSAxMjQuMjI1IDQyLjc5MjggMTI0LjM2OCA0MS45MjlDMTI0Ljk1NCAzOC43NTIgMTI3LjI4NyAzNi4yNTUgMTI4LjQ5NiAzNC42MDM3QzEyOS4wMzggMzMuOTM0NiAxMjkuMjM3IDM0LjI4NCAxMjguOTE4IDM0Ljc4MTdaTTEyOC40MTEgMzQuMDU4OEwxMjguMTM3IDM0LjM0OTlDMTI2LjkyNyAzNS42NDcyIDEyNC4xMTYgMzguODExNCAxMjMuMTc5IDQxLjcwNzRDMTIyLjk5OSA0Mi4yNDUgMTIyLjQ3NCA0Mi40ODQ4IDEyMi43MzcgNDEuNjQ5M0MxMjMuNzYzIDM4LjU4NjQgMTI2LjU4OSAzNS4yODczIDEyOC4wMTggMzMuODIyN0MxMjguNjUgMzMuMjM2NCAxMjguNzk2IDMzLjYxMDYgMTI4LjQxMSAzNC4wNTg4Wk0xMTMuODg2IDQwLjYxNjJDMTE0LjUxMyAzNy45MjMxIDExNi42MDcgMzYuNjk2IDEyMC4yMjMgMzYuOTk1M0MxMjEuMDk2IDQxLjAxNTEgMTE2LjIxMyA0Mi42MzY2IDExMy44ODYgNDAuNjE2MloiIGZpbGw9IiMwNDI0NEUiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODNfMjkzMCIgeDE9IjE0MC41IiB5MT0iMTQxIiB4Mj0iNS45OTk5OSIgeTI9Ii01LjUwMjI4ZS0wNiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjQzQ5QTZDIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0U3QkY5MyIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "users"], ["spec", "databases"], ["spec", "backup"], ["spec", "backup", "enabled"], ["spec", "backup", "s3Region"], ["spec", "backup", "s3Bucket"], ["spec", "backup", "schedule"], ["spec", "backup", "cleanupStrategy"], ["spec", "backup", "s3AccessKey"], ["spec", "backup", "s3SecretKey"], ["spec", "backup", "resticPassword"]] + secrets: + exclude: [] + include: + - resourceNames: + - mysql-{{ .name }}-credentials + services: + exclude: [] + include: + - resourceNames: + - mysql-{{ .name }}-primary + - mysql-{{ .name }}-secondary diff --git a/packages/system/mysql-rd/templates/cozyrd.yaml b/packages/system/mysql-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/mysql-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/mysql-rd/values.yaml b/packages/system/mysql-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/mysql-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/nats-rd/Chart.yaml b/packages/system/nats-rd/Chart.yaml new file mode 100644 index 00000000..9be57a87 --- /dev/null +++ b/packages/system/nats-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: nats-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/nats-rd/Makefile b/packages/system/nats-rd/Makefile new file mode 100644 index 00000000..2b5b232e --- /dev/null +++ b/packages/system/nats-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=nats-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/nats-rd/cozyrds/nats.yaml b/packages/system/nats-rd/cozyrds/nats.yaml new file mode 100644 index 00000000..258f8f40 --- /dev/null +++ b/packages/system/nats-rd/cozyrds/nats.yaml @@ -0,0 +1,40 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: nats +spec: + application: + kind: NATS + plural: natses + singular: nats + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"config":{"description":"NATS configuration.","type":"object","default":{},"properties":{"merge":{"description":"Additional configuration to merge into NATS config.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true},"resolver":{"description":"Additional resolver configuration to merge into NATS config.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"jetstream":{"description":"Jetstream configuration.","type":"object","default":{},"required":["enabled","size"],"properties":{"enabled":{"description":"Enable or disable Jetstream for persistent messaging in NATS.","type":"boolean","default":true},"size":{"description":"Jetstream persistent storage size.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"replicas":{"description":"Number of replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each NATS replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user.","type":"string"}}}}}} + release: + prefix: nats- + labels: + cozystack.io/ui: "true" + chart: + name: nats + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: NATS + plural: NATS + description: Managed NATS service + tags: + - messaging + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODFfMjgyMSkiLz4KPHJlY3Qgd2lkdGg9IjE0NCIgaGVpZ2h0PSIxNDQiIHJ4PSIyNCIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC4zIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTE3LjQ4IDI1SDI3Vjk4Ljk2OTNINjYuMDY4OUw4Ny43MDc1IDExOVY5OC45NjkzSDExNy40OFYyNVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik05Mi4xMzUyIDcyLjQ1NTJWNDIuNjI1SDEwMi43NzNWODEuMTk5OUg4Ni42NTE5TDU0LjExNCA1MC44NDE0VjgxLjIzMjJINDMuNDQ0M1Y0Mi42MjVINjAuMTI2Mkw5Mi4xMzUyIDcyLjQ1NTJaIiBmaWxsPSJibGFjayIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4MV8yODIxIiB4MT0iMTAiIHkxPSIxNS41IiB4Mj0iMTQ0IiB5Mj0iMTMxLjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzM4NUM5MyIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzMkE1NzQiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "storageClass"], ["spec", "external"], ["spec", "users"], ["spec", "jetstream"], ["spec", "jetstream", "enabled"], ["spec", "jetstream", "size"], ["spec", "config"], ["spec", "config", "merge"], ["spec", "config", "resolver"]] + secrets: + exclude: [] + include: + - resourceNames: + - nats-{{ .name }}-credentials + services: + exclude: [] + include: + - resourceNames: + - nats-{{ .name }} diff --git a/packages/system/nats-rd/templates/cozyrd.yaml b/packages/system/nats-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/nats-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/nats-rd/values.yaml b/packages/system/nats-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/nats-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/postgres-rd/Chart.yaml b/packages/system/postgres-rd/Chart.yaml new file mode 100644 index 00000000..ca2e2d2a --- /dev/null +++ b/packages/system/postgres-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: postgres-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/postgres-rd/Makefile b/packages/system/postgres-rd/Makefile new file mode 100644 index 00000000..0c8b1cb0 --- /dev/null +++ b/packages/system/postgres-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=postgres-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/postgres-rd/cozyrds/postgres.yaml b/packages/system/postgres-rd/cozyrds/postgres.yaml new file mode 100644 index 00000000..c3d16cb7 --- /dev/null +++ b/packages/system/postgres-rd/cozyrds/postgres.yaml @@ -0,0 +1,51 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: postgres +spec: + application: + kind: Postgres + singular: postgres + plural: postgreses + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["enabled"],"properties":{"destinationPath":{"description":"Destination path for backups (e.g. s3://bucket/path/).","type":"string","default":"s3://bucket/path/to/folder/"},"enabled":{"description":"Enable regular backups.","type":"boolean","default":false},"endpointURL":{"description":"S3 endpoint URL for uploads.","type":"string","default":"http://minio-gateway-service:9000"},"retentionPolicy":{"description":"Retention policy (e.g. \"30d\").","type":"string","default":"30d"},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * * *"}}},"bootstrap":{"description":"Bootstrap configuration.","type":"object","default":{},"required":["enabled","oldName"],"properties":{"enabled":{"description":"Whether to restore from a backup.","type":"boolean","default":false},"oldName":{"description":"Previous cluster name before deletion.","type":"string","default":""},"recoveryTime":{"description":"Timestamp (RFC3339) for point-in-time recovery; empty means latest.","type":"string","default":""}}},"databases":{"description":"Databases configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"extensions":{"description":"List of enabled PostgreSQL extensions.","type":"array","items":{"type":"string"}},"roles":{"description":"Roles assigned to users.","type":"object","properties":{"admin":{"description":"List of users with admin privileges.","type":"array","items":{"type":"string"}},"readonly":{"description":"List of users with read-only privileges.","type":"array","items":{"type":"string"}}}}}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"postgresql":{"description":"PostgreSQL server configuration.","type":"object","default":{},"properties":{"parameters":{"description":"PostgreSQL server parameters.","type":"object","default":{},"properties":{"max_connections":{"description":"Maximum number of concurrent connections to the database server.","type":"integer","default":100}}}}},"quorum":{"description":"Quorum configuration for synchronous replication.","type":"object","default":{},"required":["maxSyncReplicas","minSyncReplicas"],"properties":{"maxSyncReplicas":{"description":"Maximum number of synchronous replicas allowed (must be less than total replicas).","type":"integer","default":0},"minSyncReplicas":{"description":"Minimum number of synchronous replicas required for commit.","type":"integer","default":0}}},"replicas":{"description":"Number of Postgres replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each PostgreSQL replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user.","type":"string"},"replication":{"description":"Whether the user has replication privileges.","type":"boolean"}}}},"version":{"description":"PostgreSQL major version to deploy","type":"string","default":"v18","enum":["v18","v17","v16","v15","v14","v13"]}}} + release: + prefix: postgres- + labels: + cozystack.io/ui: "true" + chart: + name: postgres + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: PostgreSQL + plural: PostgreSQL + description: Managed PostgreSQL service + tags: + - database + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMjg0OSkiLz4KPHBhdGggZD0iTTk0LjQ4MSA5My4zMDQ5Qzk1LjA3MTggODguMzQ3NyA5NC44OTQ4IDg3LjYyMDggOTguNTYxMiA4OC40MjM4TDk5LjQ5MiA4OC41MDYxQzEwMi4zMTEgODguNjM1MyAxMDUuOTk5IDg4LjA0OTUgMTA4LjE2NiA4Ny4wMzU4QzExMi44MyA4NC44NTY0IDExNS41OTUgODEuMjE3NiAxMTAuOTk2IDgyLjE3MzhDMTAwLjUwNiA4NC4zNTMyIDk5Ljc4NDkgODAuNzc1OSA5OS43ODQ5IDgwLjc3NTlDMTEwLjg2MiA2NC4yMjQgMTE1LjQ5MyA0My4yMTI4IDExMS40OTYgMzguMDY5N0MxMDAuNTk0IDI0LjA0MTIgODEuNzIzMSAzMC42NzUgODEuNDA3MyAzMC44NDcyTDgxLjMwNjggMzAuODY1OUM3OS4yMzQgMzAuNDMyOCA3Ni45MTQzIDMwLjE3NCA3NC4zMDg1IDMwLjEzMTZDNjkuNTYxMyAzMC4wNTMgNjUuOTU5MSAzMS4zODQ5IDYzLjIyNjYgMzMuNDcyMUM2My4yMjY2IDMzLjQ3MjEgMjkuNTYyMSAxOS41MDQ3IDMxLjEyODIgNTEuMDM3NUMzMS40NjEzIDU3Ljc0NTQgNDAuNjc1OCAxMDEuNzk1IDUxLjY2NTkgODguNDkwMUM1NS42ODI3IDgzLjYyNDkgNTkuNTY0NiA3OS41MTEzIDU5LjU2NDYgNzkuNTExM0M2MS40OTIyIDgwLjgwMDkgNjMuOCA4MS40NTg4IDY2LjIyMDQgODEuMjIyNUw2Ni40MDc1IDgxLjA2MThDNjYuMzQ4OSA4MS42NjU5IDY2LjM3NDcgODIuMjU2NyA2Ni40ODI0IDgyLjk1NjJDNjMuNjUxNyA4Ni4xNDE5IDY0LjQ4MzUgODYuNzAxMiA1OC44MjMxIDg3Ljg3NDVDNTMuMDk2NSA4OS4wNjMxIDU2LjQ2MDkgOTEuMTc5MyA1OC42NTY5IDkxLjczMjRDNjEuMzE5OSA5Mi40MDMgNjcuNDgwNCA5My4zNTMgNzEuNjQ0IDg3LjQ4NDVMNzEuNDc4MiA4OC4xNTQxQzcyLjU4ODggODkuMDQ4OSA3Mi41MTM3IDk0LjU4NTQgNzIuNjcxMiA5OC41NDExQzcyLjgyODkgMTAyLjQ5NyA3My4wOTE5IDEwNi4xODkgNzMuODkyNiAxMDguMzY1Qzc0LjY5MzMgMTEwLjU0MSA3NS42MzgxIDExNi4xNDcgODMuMDc3MSAxMTQuNTQxQzg5LjI5NDMgMTEzLjIgOTQuMDQ3OCAxMTEuMjY5IDk0LjQ4MSA5My4zMDQ5WiIgZmlsbD0iYmxhY2siIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iNiIvPgo8cGF0aCBkPSJNMTEwLjk5OCA4Mi4xNzI3QzEwMC41MDYgODQuMzUyMSA5OS43ODQ5IDgwLjc3NDggOTkuNzg0OSA4MC43NzQ4QzExMC44NjIgNjQuMjIxOCAxMTUuNDkzIDQzLjIxMDIgMTExLjQ5NyAzOC4wNjc4QzEwMC41OTUgMjQuMDQwMSA4MS43MjMxIDMwLjY3NDMgODEuNDA4MiAzMC44NDY1TDgxLjMwNjggMzAuODY0OEM3OS4yMzQxIDMwLjQzMTUgNzYuOTE0NCAzMC4xNzMzIDc0LjMwNzMgMzAuMTMwNUM2OS41NiAzMC4wNTIxIDY1Ljk1OTEgMzEuMzgzOCA2My4yMjY3IDMzLjQ3MDZDNjMuMjI2NyAzMy40NzA2IDI5LjU2MTUgMTkuNTAzOCAzMS4xMjcyIDUxLjAzNjRDMzEuNDYwMyA1Ny43NDQ3IDQwLjY3NDYgMTAxLjc5NSA1MS42NjUxIDg4LjQ4OTVDNTUuNjgyMSA4My42MjQzIDU5LjU2MzQgNzkuNTEwNiA1OS41NjM0IDc5LjUxMDZDNjEuNDkxMiA4MC44MDAyIDYzLjc5OSA4MS40NTgxIDY2LjIxODQgODEuMjIxOEw2Ni40MDYzIDgxLjA2MTFDNjYuMzQ3OSA4MS42NjUyIDY2LjM3NDYgODIuMjU2IDY2LjQ4MTYgODIuOTU1NUM2My42NTAzIDg2LjE0MTIgNjQuNDgyMiA4Ni43MDA1IDU4LjgyMjMgODcuODczOEM1My4wOTUzIDg5LjA2MjUgNTYuNDU5NyA5MS4xNzg2IDU4LjY1NjMgOTEuNzMxN0M2MS4zMTkzIDkyLjQwMjMgNjcuNDgwMiA5My4zNTI0IDcxLjY0MyA4Ny40ODM4TDcxLjQ3NyA4OC4xNTM0QzcyLjU4NjQgODkuMDQ4MiA3My4zNjU0IDkzLjk3MzkgNzMuMjM0OCA5OC40MzlDNzMuMTA0MiAxMDIuOTA0IDczLjAxNzEgMTA1Ljk3IDczLjg5MTIgMTA4LjM2NEM3NC43NjUzIDExMC43NTkgNzUuNjM2NSAxMTYuMTQ2IDgzLjA3NjkgMTE0LjU0MUM4OS4yOTQxIDExMy4xOTkgOTIuNTE1OSAxMDkuNzIyIDkyLjk2NDEgMTAzLjkyMkM5My4yODIyIDk5Ljc5ODggOTQuMDAxOSAxMDAuNDA4IDk0LjA0NzQgOTYuNzIxOUw5NC42MjQ3IDk0Ljk3NjZDOTUuMjkwNSA4OS4zODcyIDk0LjczMDUgODcuNTg0IDk4LjU2MDggODguNDIyN0w5OS40OTE3IDg4LjUwNUMxMDIuMzExIDg4LjYzNDIgMTA2LjAwMSA4OC4wNDg0IDEwOC4xNjYgODcuMDM0N0MxMTIuODI5IDg0Ljg1NTMgMTE1LjU5NSA4MS4yMTY2IDExMC45OTcgODIuMTcyN0gxMTAuOTk4WiIgZmlsbD0iIzMzNjc5MSIvPgo8cGF0aCBkPSJNNzIuMDkzMyA4NS4zNzdDNzEuODA0NSA5NS43Nzc0IDcyLjE2NTkgMTA2LjI1IDczLjE3NjQgMTA4Ljc5NkM3NC4xODc2IDExMS4zNDEgNzYuMzUxNCAxMTYuMjkyIDgzLjc5MjUgMTE0LjY4NkM5MC4wMDkxIDExMy4zNDQgOTIuMjcxIDExMC43NDcgOTMuMjUyNiAxMDUuMDE0QzkzLjk3NTUgMTAwLjc5NiA5NS4zNjkxIDg5LjA4MSA5NS41NDc5IDg2LjY4MDkiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik02My4xNzUgMzMuMjM5M0M2My4xNzUgMzMuMjM5MyAyOS40ODY4IDE5LjM3MzIgMzEuMDUzIDUwLjkwNThDMzEuMzg2IDU3LjYxNDEgNDAuNjAxIDEwMS42NjUgNTEuNTkxMyA4OC4zNTk3QzU1LjYwNzUgODMuNDkzOCA1OS4yMzk3IDc5LjY3NzYgNTkuMjM5NyA3OS42Nzc2IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNODEuMzcxMyAzMC43MDc4QzgwLjIwNTIgMzEuMDc2IDEwMC4xMTEgMjMuMzc5NiAxMTEuNDIzIDM3LjkzNjhDMTE1LjQxOSA0My4wNzk1IDExMC43ODkgNjQuMDkxMSA5OS43MTE1IDgwLjY0NDUiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik05OS43MTEgODAuNjQ0OEM5OS43MTEgODAuNjQ0OCAxMDAuNDMzIDg0LjIyMyAxMTAuOTI0IDgyLjA0MjJDMTE1LjUyMSA4MS4wODYxIDExMi43NTUgODQuNzI1MiAxMDguMDkzIDg2LjkwNTdDMTA0LjI2NyA4OC42OTQgOTUuNjg4MyA4OS4xNTIzIDk1LjU0ODIgODYuNjgxMkM5NS4xODc2IDgwLjMwNTMgMTAwLjA2MyA4Mi4yNDIzIDk5LjcxMSA4MC42NDQ4Wk05OS43MTEgODAuNjQ0OEM5OS4zOTI5IDc5LjIwNiA5Ny4yMTI4IDc3Ljc5MzkgOTUuNzcwNSA3NC4yNzI1Qzk0LjUxMTQgNzEuMTk5IDc4LjUwMTkgNDcuNjI4OSAxMDAuMjEgNTEuMTI5NEMxMDEuMDA2IDUwLjk2MzcgOTQuNTQ4NSAzMC4zMzQ4IDc0LjIzMjUgMjkuOTk5NEM1My45MjExIDI5LjY2MzkgNTQuNTg3NSA1NS4xNTQ1IDU0LjU4NzUgNTUuMTU0NSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0iYmV2ZWwiLz4KPHBhdGggZD0iTTY2LjQwNzcgODIuODI1M0M2My41NzYgODYuMDEwOCA2NC40MDg4IDg2LjU3MDIgNTguNzQ4NSA4Ny43NDM5QzUzLjAyMTQgODguOTMyNyA1Ni4zODYyIDkxLjA0ODUgNTguNTgyMiA5MS42MDEzQzYxLjI0NTIgOTIuMjcyNCA2Ny40MDYxIDkzLjIyMjQgNzEuNTY4OSA4Ny4zNTI0QzcyLjgzNjYgODUuNTY1MSA3MS41NjE0IDgyLjcxMzQgNjkuODIwMSA4MS45ODY0QzY4Ljk3ODcgODEuNjM1NCA2Ny44NTM3IDgxLjE5NTYgNjYuNDA3NyA4Mi44MjUzWiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTY2LjIyMjUgODIuNzY5MUM2NS45MzcyIDgwLjg5NjEgNjYuODMzNiA3OC42Njc0IDY3Ljc5NDMgNzYuMDU5OUM2OS4yMzggNzIuMTQ3NyA3Mi41NjkgNjguMjM0OCA2OS45MDQ0IDU1LjgyNDZDNjcuOTE4MiA0Ni41NzY3IDU0LjU5NjMgNTMuOSA1NC41ODggNTUuMTU0QzU0LjU3OTggNTYuNDA3NSA1NS4xOTA1IDYxLjUwOTkgNTQuMzY1NCA2Ny40NTE1QzUzLjI4ODggNzUuMjA0OCA1OS4yNjQzIDgxLjc2MjEgNjYuMTQ1MSA4MS4wOTEzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNNjMuMDUyOCA1NC45NjY1QzYyLjk5MjggNTUuMzk0OCA2My44MzE0IDU2LjUzNzcgNjQuOTI0OSA1Ni42OTA0QzY2LjAxNjYgNTYuODQzNyA2Ni45NTEgNTUuOTUwNiA2Ny4wMTAyIDU1LjUyMjdDNjcuMDY5NCA1NS4wOTQ1IDY2LjIzMTggNTQuNjIyNyA2NS4xMzc5IDU0LjQ2OTRDNjQuMDQ1NiA1NC4zMTU4IDYzLjExMDggNTQuNTM5MyA2My4wNTMxIDU0Ljk2NjVINjMuMDUyOFoiIGZpbGw9IndoaXRlIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz4KPHBhdGggZD0iTTk2LjMwMzQgNTQuMDkyNEM5Ni4zNjI3IDU0LjUyMDcgOTUuNTI1MSA1NS42NjM1IDk0LjQzMTMgNTUuODE2MkM5My4zMzg5IDU1Ljk2OTYgOTIuNDA0NSA1NS4wNzY1IDkyLjM0NDYgNTQuNjQ4NkM5Mi4yODY4IDU0LjIyMDMgOTMuMTI0NyA1My43NDg2IDk0LjIxNzMgNTMuNTk1M0M5NS4zMSA1My40NDE5IDk2LjI0NDIgNTMuNjY1MiA5Ni4zMDM0IDU0LjA5MjZWNTQuMDkyNFoiIGZpbGw9IndoaXRlIiBzdHJva2U9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMDAuMjEgNTEuMTI4OUMxMDAuMzkgNTQuNDg4MyA5OS40OTIgNTYuNzc2NSA5OS4zNzg3IDYwLjM1MjdDOTkuMjExIDY1LjU1MDggMTAxLjg0IDcxLjUwMDUgOTcuODc4OSA3Ny40NTc1IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4M18yODQ5IiB4MT0iMTQwIiB5MT0iMTMwLjUiIHgyPSI0IiB5Mj0iOS40OTk5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjMDAyQzRDIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwNDc3QiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= + #tabs: + # services: + # labelSelector: + # cnpg.io/cluster: "{reqs[0]['metadata','name']}" + # workloadMonitors: + # labelSelector: + # helm.toolkit.fluxcd.io/name: "{reqs[0]['metadata','name']}" + + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "postgresql"], ["spec", "postgresql", "parameters"], ["spec", "postgresql", "parameters", "max_connections"], ["spec", "quorum"], ["spec", "quorum", "minSyncReplicas"], ["spec", "quorum", "maxSyncReplicas"], ["spec", "users"], ["spec", "databases"], ["spec", "backup"], ["spec", "backup", "enabled"], ["spec", "backup", "retentionPolicy"], ["spec", "backup", "destinationPath"], ["spec", "backup", "endpointURL"], ["spec", "backup", "schedule"], ["spec", "backup", "s3AccessKey"], ["spec", "backup", "s3SecretKey"], ["spec", "bootstrap"], ["spec", "bootstrap", "enabled"], ["spec", "bootstrap", "recoveryTime"], ["spec", "bootstrap", "oldName"]] + secrets: + exclude: [] + include: + - resourceNames: + - postgres-{{ .name }}-credentials + services: + exclude: [] + include: + - resourceNames: + - postgres-{{ .name }}-r + - postgres-{{ .name }}-ro + - postgres-{{ .name }}-rw + - postgres-{{ .name }}-external-write diff --git a/packages/system/postgres-rd/templates/cozyrd.yaml b/packages/system/postgres-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/postgres-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/postgres-rd/values.yaml b/packages/system/postgres-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/postgres-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/rabbitmq-rd/Chart.yaml b/packages/system/rabbitmq-rd/Chart.yaml new file mode 100644 index 00000000..9fa858de --- /dev/null +++ b/packages/system/rabbitmq-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: rabbitmq-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/rabbitmq-rd/Makefile b/packages/system/rabbitmq-rd/Makefile new file mode 100644 index 00000000..9c5be0d5 --- /dev/null +++ b/packages/system/rabbitmq-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=rabbitmq-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml b/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml new file mode 100644 index 00000000..092142ac --- /dev/null +++ b/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml @@ -0,0 +1,42 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: rabbitmq +spec: + application: + kind: RabbitMQ + plural: rabbitmqs + singular: rabbitmq + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"replicas":{"description":"Number of RabbitMQ replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each RabbitMQ replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user.","type":"string"}}}},"vhosts":{"description":"Virtual hosts configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","required":["roles"],"properties":{"roles":{"description":"Virtual host roles list.","type":"object","properties":{"admin":{"description":"List of admin users.","type":"array","items":{"type":"string"}},"readonly":{"description":"List of readonly users.","type":"array","items":{"type":"string"}}}}}}}}} + release: + prefix: rabbitmq- + labels: + cozystack.io/ui: "true" + chart: + name: rabbitmq + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: RabbitMQ + plural: RabbitMQ + description: Managed RabbitMQ service + tags: + - messaging + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9Ii0wLjAwMTk1MzEyIiB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgcng9IjI0IiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfNjgzXzI5NzIpIi8+CjxwYXRoIGQ9Ik0xMTEuNDExIDYyLjhIODIuNDkzOUM4MS43OTY5IDYyLjc5OTcgODEuMTI4NSA2Mi41MjI4IDgwLjYzNTYgNjIuMDMwMUM4MC4xNDI3IDYxLjUzNzMgNzkuODY1NiA2MC44NjkxIDc5Ljg2NTMgNjAuMTcyMlYzMC4wNDEyQzc5Ljg2NTMgMjcuODEgNzguMDU1NiAyNiA3NS44MjQ5IDI2SDY1LjUwMjFDNjMuMjcgMjYgNjEuNDYxNCAyNy44MSA2MS40NjE0IDMwLjA0MTJWNTkuOTg5OEM2MS40NjE0IDYxLjU0MzUgNjAuMjA1IDYyLjgwNjUgNTguNjUwOCA2Mi44MTMzTDQ5LjE3NDMgNjIuODU4NEM0Ny42MDY5IDYyLjg2NjkgNDYuMzMzNiA2MS41OTU1IDQ2LjMzNjYgNjAuMDI5OEw0Ni4zOTU0IDMwLjA0OEM0Ni40MDA1IDI3LjgxMzQgNDQuNTkwMiAyNiA0Mi4zNTUgMjZIMzIuMDQwN0MyOS44MDgzIDI2IDI4IDI3LjgxIDI4IDMwLjA0MTJWMTE0LjQxMkMyOCAxMTYuMzk0IDI5LjYwNjEgMTE4IDMxLjU4NzEgMTE4SDExMS40MTFDMTEzLjM5NCAxMTggMTE1IDExNi4zOTQgMTE1IDExNC40MTJWNjYuMzg4QzExNSA2NC40MDU4IDExMy4zOTQgNjIuOCAxMTEuNDExIDYyLjhaTTk3Ljg1MDggOTQuNDc3OUM5Ny44NTA4IDk3LjA3NTUgOTUuNzQ0NSA5OS4xODE3IDkzLjE0NjQgOTkuMTgxN0g4NC45ODg0QzgyLjM5IDk5LjE4MTcgODAuMjgzNiA5Ny4wNzU1IDgwLjI4MzYgOTQuNDc3OVY4Ni4zMjE3QzgwLjI4MzYgODMuNzIzOCA4Mi4zOSA4MS42MTc5IDg0Ljk4ODQgODEuNjE3OUg5My4xNDY0Qzk1Ljc0NDUgODEuNjE3OSA5Ny44NTA4IDgzLjcyMzggOTcuODUwOCA4Ni4zMjE3Vjk0LjQ3NzlaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4M18yOTcyIiB4MT0iNSIgeTE9Ii03LjUiIHgyPSIxNDEiIHkyPSIxMjQuNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkY4MjJGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0ZGNjYwMCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "users"], ["spec", "vhosts"]] + secrets: + exclude: [] + include: + - resourceNames: + - rabbitmq-{{ .name }}-default-user + - matchLabels: + apps.cozystack.io/user-secret: "true" + services: + exclude: [] + include: + - resourceNames: + - rabbitmq-{{ .name }} diff --git a/packages/system/rabbitmq-rd/templates/cozyrd.yaml b/packages/system/rabbitmq-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/rabbitmq-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/rabbitmq-rd/values.yaml b/packages/system/rabbitmq-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/rabbitmq-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/redis-rd/Chart.yaml b/packages/system/redis-rd/Chart.yaml new file mode 100644 index 00000000..4ccfa4fd --- /dev/null +++ b/packages/system/redis-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: redis-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/redis-rd/Makefile b/packages/system/redis-rd/Makefile new file mode 100644 index 00000000..bed18877 --- /dev/null +++ b/packages/system/redis-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=redis-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/redis-rd/cozyrds/redis.yaml b/packages/system/redis-rd/cozyrds/redis.yaml new file mode 100644 index 00000000..d23f8d2c --- /dev/null +++ b/packages/system/redis-rd/cozyrds/redis.yaml @@ -0,0 +1,43 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: redis +spec: + application: + kind: Redis + plural: redises + singular: redis + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"authEnabled":{"description":"Enable password generation.","type":"boolean","default":true},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"replicas":{"description":"Number of Redis replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each Redis replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"version":{"description":"Redis major version to deploy","type":"string","default":"v8","enum":["v8","v7"]}}} + release: + prefix: redis- + labels: + cozystack.io/ui: "true" + chart: + name: redis + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: PaaS + singular: Redis + plural: Redis + description: Managed Redis service + tags: + - cache + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMzIxMykiLz4KPHBhdGggZD0iTTEyMC4xNDkgOTUuNTQ5MUMxMTQuNTg2IDk4LjQ0ODUgODUuNzcwOSAxMTAuMjk2IDc5LjYzNjMgMTEzLjQ5NUM3My41MDE2IDExNi42OTMgNzAuMDkzNyAxMTYuNjYyIDY1LjI0NzIgMTE0LjM0NkM2MC40MDEyIDExMi4wMjkgMjkuNzM2NCA5OS42NDIzIDI0LjIxMjUgOTcuMDAxOUMyMS40NTE5IDk1LjY4MjcgMjAgOTQuNTY4NyAyMCA5My41MTY2VjgyLjk4MDlDMjAgODIuOTgwOSA1OS45MjIgNzQuMjkwMSA2Ni4zNjY5IDcxLjk3NzhDNzIuODExNSA2OS42NjU2IDc1LjA0NzYgNjkuNTgyMSA4MC41MzIgNzEuNTkxQzg2LjAxNzMgNzMuNjAwOCAxMTguODEyIDc5LjUxNzYgMTI0LjIzMyA4MS41MDI5TDEyNC4yMyA5MS44ODk2QzEyNC4yMzEgOTIuOTMxMSAxMjIuOTggOTQuMDczNiAxMjAuMTQ5IDk1LjU0OTFaIiBmaWxsPSIjOTEyNjI2Ii8+CjxwYXRoIGQ9Ik0xMjAuMTQ3IDg1LjA3NTJDMTE0LjU4NSA4Ny45NzM0IDg1Ljc3IDk5LjgyMTggNzkuNjM1NCAxMDMuMDJDNzMuNTAxMSAxMDYuMjE5IDcwLjA5MzIgMTA2LjE4NyA2NS4yNDcyIDEwMy44NzFDNjAuNDAwNyAxMDEuNTU1IDI5LjczNzEgODkuMTY2OCAyNC4yMTM2IDg2LjUyOEMxOC42OTAxIDgzLjg4NzYgMTguNTc0NCA4Mi4wNzA0IDI0LjAwMDMgNzkuOTQ1OEMyOS40MjYxIDc3LjgyMDUgNTkuOTIxNSA2NS44NTYxIDY2LjM2NzIgNjMuNTQzOEM3Mi44MTE4IDYxLjIzMjQgNzUuMDQ3NSA2MS4xNDgxIDgwLjUzMTkgNjMuMTU3OEM4Ni4wMTY4IDY1LjE2NjggMTE0LjY2IDc2LjU2NzYgMTIwLjA3OSA3OC41NTI1QzEyNS41MDEgODAuNTM5OSAxMjUuNzA5IDgyLjE3NjMgMTIwLjE0NyA4NS4wNzUyWiIgZmlsbD0iI0M2MzAyQiIvPgo8cGF0aCBkPSJNMTIwLjE0OSA3OC41MDJDMTE0LjU4NiA4MS40MDE4IDg1Ljc3MDkgOTMuMjQ5MyA3OS42MzYzIDk2LjQ0ODhDNzMuNTAxNiA5OS42NDYyIDcwLjA5MzcgOTkuNjE1MiA2NS4yNDcyIDk3LjI5ODVDNjAuNDAwOCA5NC45ODMgMjkuNzM2NCA4Mi41OTUyIDI0LjIxMjUgNzkuOTU0N0MyMS40NTE5IDc4LjYzNTUgMjAgNzcuNTIzMiAyMCA3Ni40NzA3VjY1LjkzMzhDMjAgNjUuOTMzOCA1OS45MjIgNTcuMjQzNCA2Ni4zNjY5IDU0LjkzMTFDNzIuODExNSA1Mi42MTg5IDc1LjA0NzYgNTIuNTM1IDgwLjUzMiA1NC41NDQzQzg2LjAxNzcgNTYuNTUzNiAxMTguODEzIDYyLjQ2OTMgMTI0LjIzMyA2NC40NTVMMTI0LjIzIDc0Ljg0MjhDMTI0LjIzMSA3NS44ODQgMTIyLjk4IDc3LjAyNjQgMTIwLjE0OSA3OC41MDJaIiBmaWxsPSIjOTEyNjI2Ii8+CjxwYXRoIGQ9Ik0xMjAuMTQ3IDY4LjAyODJDMTE0LjU4NSA3MC45MjcxIDg1Ljc3IDgyLjc3NDcgNzkuNjM1NCA4NS45NzM3QzczLjUwMTEgODkuMTcxNiA3MC4wOTMyIDg5LjE0MDIgNjUuMjQ3MiA4Ni44MjM1QzYwLjQwMDcgODQuNTA4NCAyOS43MzcxIDcyLjEyMDEgMjQuMjEzNiA2OS40ODA5QzE4LjY5MDEgNjYuODQxMyAxOC41NzQ0IDY1LjAyMzcgMjQuMDAwMyA2Mi44OTg0QzI5LjQyNjEgNjAuNzc0MiA1OS45MjE5IDQ4LjgwOSA2Ni4zNjcyIDQ2LjQ5NzJDNzIuODExOCA0NC4xODUzIDc1LjA0NzUgNDQuMTAxNCA4MC41MzE5IDQ2LjExMDhDODYuMDE2OCA0OC4xMTk3IDExNC42NiA1OS41MTk3IDEyMC4wNzkgNjEuNTA1NUMxMjUuNTAxIDYzLjQ5MjQgMTI1LjcwOSA2NS4xMjkyIDEyMC4xNDcgNjguMDI4MloiIGZpbGw9IiNDNjMwMkIiLz4KPHBhdGggZD0iTTEyMC4xNDkgNjAuODIyNEMxMTQuNTg2IDYzLjcyMTQgODUuNzcwOSA3NS41Njk4IDc5LjYzNjMgNzguNzY5MkM3My41MDE2IDgxLjk2NzEgNzAuMDkzNyA4MS45MzU3IDY1LjI0NzIgNzkuNjE5QzYwLjQwMDggNzcuMzAzNSAyOS43MzY0IDY0LjkxNTIgMjQuMjEyNSA2Mi4yNzZDMjEuNDUxOSA2MC45NTU2IDIwIDU5Ljg0MjggMjAgNTguNzkxNVY0OC4yNTQyQzIwIDQ4LjI1NDIgNTkuOTIyIDM5LjU2NDIgNjYuMzY2OSAzNy4yNTI0QzcyLjgxMTUgMzQuOTM5NyA3NS4wNDc2IDM0Ljg1NjcgODAuNTMyIDM2Ljg2NTZDODYuMDE3NyAzOC44NzQ5IDExOC44MTMgNDQuNzkwNSAxMjQuMjMzIDQ2Ljc3NjNMMTI0LjIzIDU3LjE2MzdDMTI0LjIzMSA1OC4yMDQgMTIyLjk4IDU5LjM0NjUgMTIwLjE0OSA2MC44MjI0WiIgZmlsbD0iIzkxMjYyNiIvPgo8cGF0aCBkPSJNMTIwLjE0NyA1MC4zNDlDMTE0LjU4NSA1My4yNDc5IDg1Ljc2OTggNjUuMDk2MyA3OS42MzUyIDY4LjI5NDFDNzMuNTAwOSA3MS40OTIgNzAuMDkzIDcxLjQ2MDYgNjUuMjQ2OSA2OS4xNDUxQzYwLjQwMDkgNjYuODI4MyAyOS43MzY5IDU0LjQ0MDkgMjQuMjEzOCA1MS44MDEzQzE4LjY4OTkgNDkuMTYyMSAxOC41NzQ2IDQ3LjM0NDEgMjQgNDUuMjE5MkMyOS40MjU5IDQzLjA5NDYgNTkuOTIxNyAzMS4xMzEgNjYuMzY3IDI4LjgxODRDNzIuODExNiAyNi41MDYxIDc1LjA0NzMgMjYuNDIzIDgwLjUzMTcgMjguNDMyNEM4Ni4wMTY2IDMwLjQ0MTcgMTE0LjY1OSA0MS44NDE4IDEyMC4wNzkgNDMuODI3NUMxMjUuNTAxIDQ1LjgxMjggMTI1LjcwOSA0Ny40NSAxMjAuMTQ3IDUwLjM0OVoiIGZpbGw9IiNDNjMwMkIiLz4KPHBhdGggZD0iTTg0Ljg1NDEgNDAuMDk5NEw3NS44OTI2IDQxLjAyOThMNzMuODg2NSA0NS44NTdMNzAuNjQ2MyA0MC40NzAzTDYwLjI5ODMgMzkuNTQwNEw2OC4wMTk3IDM2Ljc1NThMNjUuNzAzIDMyLjQ4MTRMNzIuOTMyMSAzNS4zMDg4TDc5Ljc0NzEgMzMuMDc3NUw3Ny45MDUyIDM3LjQ5NzJMODQuODU0MSA0MC4wOTk0Wk03My4zNTE1IDYzLjUxODRMNTYuNjI2NiA1Ni41ODE2TDgwLjU5MiA1Mi45MDI5TDczLjM1MTUgNjMuNTE4NFpNNTAuMTYzNyA0Mi43ODI2QzU3LjIzODEgNDIuNzgyNiA2Mi45NzMgNDUuMDA1NyA2Mi45NzMgNDcuNzQ3NUM2Mi45NzMgNTAuNDkwMSA1Ny4yMzgxIDUyLjcxMjggNTAuMTYzNyA1Mi43MTI4QzQzLjA4OTMgNTIuNzEyOCAzNy4zNTQ1IDUwLjQ4OTcgMzcuMzU0NSA0Ny43NDc1QzM3LjM1NDUgNDUuMDA1NyA0My4wODkzIDQyLjc4MjYgNTAuMTYzNyA0Mi43ODI2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTk1LjQ0MzQgNDEuNDE3NEwxMDkuNjI3IDQ3LjAyMjRMOTUuNDU1NiA1Mi42MjJMOTUuNDQzNCA0MS40MTc0WiIgZmlsbD0iIzYyMUIxQyIvPgo8cGF0aCBkPSJNNzkuNzUyOSA0Ny42MjYxTDk1LjQ0NDkgNDEuNDE4OUw5NS40NTcxIDUyLjYyMzZMOTMuOTE4NCA1My4yMjU0TDc5Ljc1MjkgNDcuNjI2MVoiIGZpbGw9IiM5QTI5MjgiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODNfMzIxMyIgeDE9IjE4OSIgeTE9IjIxMC41IiB4Mj0iMCIgeTI9IjAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0E4MDAwMCIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkNGQ0YiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "authEnabled"]] + secrets: + exclude: [] + include: + - resourceNames: + - redis-{{ .name }}-auth + services: + exclude: [] + include: + - resourceNames: + - rfs-redis-{{ .name }} + - rfrm-redis-{{ .name }} + - rfrs-redis-{{ .name }} + - redis-{{ .name }}-external-lb diff --git a/packages/system/redis-rd/templates/cozyrd.yaml b/packages/system/redis-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/redis-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/redis-rd/values.yaml b/packages/system/redis-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/redis-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/seaweedfs-rd/Chart.yaml b/packages/system/seaweedfs-rd/Chart.yaml new file mode 100644 index 00000000..5799783b --- /dev/null +++ b/packages/system/seaweedfs-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: seaweedfs-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/seaweedfs-rd/Makefile b/packages/system/seaweedfs-rd/Makefile new file mode 100644 index 00000000..5be03dbb --- /dev/null +++ b/packages/system/seaweedfs-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=seaweedfs-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml b/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml new file mode 100644 index 00000000..787b5448 --- /dev/null +++ b/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml @@ -0,0 +1,44 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: seaweedfs +spec: + application: + kind: SeaweedFS + singular: seaweedfs + plural: seaweedfses + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"db":{"description":"Database configuration.","type":"object","default":{},"properties":{"replicas":{"description":"Number of database replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume size.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""}}},"filer":{"description":"Filer service configuration.","type":"object","default":{},"properties":{"grpcHost":{"description":"The hostname used to expose or access the filer service externally.","type":"string","default":""},"grpcPort":{"description":"The port used to access the filer service externally.","type":"integer","default":443},"replicas":{"description":"Number of filer replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"whitelist":{"description":"A list of IP addresses or CIDR ranges that are allowed to access the filer service.","type":"array","default":[],"items":{"type":"string"}}}},"host":{"description":"The hostname used to access SeaweedFS externally (defaults to 's3' subdomain for the tenant host).","type":"string","default":""},"master":{"description":"Master service configuration.","type":"object","default":{},"properties":{"replicas":{"description":"Number of master replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"replicationFactor":{"description":"Replication factor: number of replicas for each volume in the SeaweedFS cluster.","type":"integer","default":2},"s3":{"description":"S3 service configuration.","type":"object","default":{},"properties":{"replicas":{"description":"Number of S3 replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"topology":{"description":"The topology of the SeaweedFS cluster.","type":"string","default":"Simple","enum":["Simple","MultiZone","Client"]},"volume":{"description":"Volume service configuration.","type":"object","default":{},"properties":{"replicas":{"description":"Number of volume replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume size.","default":"10Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"zones":{"description":"A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"replicas":{"description":"Number of replicas in the zone.","type":"integer"},"size":{"description":"Zone storage size.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}}}}} + release: + prefix: "" + labels: + cozystack.io/ui: "true" + internal.cozystack.io/tenantmodule: "true" + chart: + name: seaweedfs + sourceRef: + kind: HelmRepository + name: cozystack-extra + namespace: cozy-public + dashboard: + category: Administration + singular: SeaweedFS + plural: SeaweedFS + name: seaweedfs + description: Seaweedfs + module: true + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82NzlfMTkxMCkiLz4KPHBhdGggZD0iTTEzOC42ODUgMTIxLjA1N0MxMzguNjg1IDEyNi42NTIgMTM2LjQ2MiAxMzIuMDE3IDEzMi41MDQgMTM1Ljk3M0MxMjguNTQ3IDEzOS45MjkgMTIzLjE3OSAxNDIuMTUxIDExNy41ODIgMTQyLjE1MUMxMTEuOTg1IDE0Mi4xNTEgMTA2LjYxOCAxMzkuOTI5IDEwMi42NiAxMzUuOTczQzk4LjcwMjggMTMyLjAxNyA5Ni40Nzk1IDEyNi42NTIgOTYuNDc5NSAxMjEuMDU3Qzk2LjQ3OTUgMTE4LjI4NyA5Ny4wMjUzIDExNS41NDQgOTguMDg1OCAxMTIuOTg1Qzk5LjE0NjMgMTEwLjQyNSAxMDAuNzAxIDEwOC4xIDEwMi42NiAxMDYuMTQxQzEwNC42MiAxMDQuMTgyIDEwNi45NDYgMTAyLjYyOSAxMDkuNTA3IDEwMS41NjlDMTEyLjA2NyAxMDAuNTA5IDExNC44MTEgOTkuOTYyOSAxMTcuNTgyIDk5Ljk2MjlDMTIwLjM1MyA5OS45NjI5IDEyMy4wOTggMTAwLjUwOSAxMjUuNjU4IDEwMS41NjlDMTI4LjIxOCAxMDIuNjI5IDEzMC41NDQgMTA0LjE4MiAxMzIuNTA0IDEwNi4xNDFDMTM0LjQ2NCAxMDguMSAxMzYuMDE4IDExMC40MjUgMTM3LjA3OSAxMTIuOTg1QzEzOC4xMzkgMTE1LjU0NCAxMzguNjg1IDExOC4yODcgMTM4LjY4NSAxMjEuMDU3WiIgZmlsbD0idXJsKCNwYWludDFfcmFkaWFsXzY3OV8xOTEwKSIvPgo8cGF0aCBkPSJNMTEwLjMzNiAxMjYuMTQ3SDEyMy42OFYxMzYuODIzSDExMC4zMzZWMTI2LjE0N1oiIGZpbGw9IiMwRjVFOUMiLz4KPHBhdGggZD0iTTExOS4yNyAxMTIuMjk0QzExOS4yNyAxMTIuNzE0IDExOS4xNzggMTEzLjEzMSAxMTkgMTEzLjUxOUMxMTguODIxIDExMy45MDggMTE4LjU2IDExNC4yNjEgMTE4LjIzIDExNC41NThDMTE3LjkwMSAxMTQuODU1IDExNy41MDkgMTE1LjA5MSAxMTcuMDc5IDExNS4yNTJDMTE2LjY0OCAxMTUuNDEzIDExNi4xODYgMTE1LjQ5NiAxMTUuNzIgMTE1LjQ5NkMxMTQuNzc4IDExNS40OTYgMTEzLjg3NSAxMTUuMTU4IDExMy4yMSAxMTQuNTU4QzExMi41NDQgMTEzLjk1NyAxMTIuMTcgMTEzLjE0MyAxMTIuMTcgMTEyLjI5NEMxMTIuMTcgMTExLjQ0NSAxMTIuNTQ0IDExMC42MyAxMTMuMjEgMTEwLjAzQzExMy44NzUgMTA5LjQyOSAxMTQuNzc4IDEwOS4wOTIgMTE1LjcyIDEwOS4wOTJDMTE2LjE4NiAxMDkuMDkyIDExNi42NDggMTA5LjE3NSAxMTcuMDc5IDEwOS4zMzZDMTE3LjUwOSAxMDkuNDk2IDExNy45MDEgMTA5LjczMiAxMTguMjMgMTEwLjAzQzExOC41NiAxMTAuMzI3IDExOC44MjEgMTEwLjY4IDExOSAxMTEuMDY4QzExOS4xNzggMTExLjQ1NyAxMTkuMjcgMTExLjg3MyAxMTkuMjcgMTEyLjI5NFoiIGZpbGw9IiM1OTY4NkYiLz4KPHBhdGggZD0iTTEyOC4xMSAxMTQuMTAzQzEyOC4xMSAxMTQuOTUzIDEyNy43MzYgMTE1Ljc2NyAxMjcuMDcgMTE2LjM2OEMxMjYuNDA0IDExNi45NjggMTI1LjUwMSAxMTcuMzA1IDEyNC41NiAxMTcuMzA1QzEyNC4wOTQgMTE3LjMwNSAxMjMuNjMyIDExNy4yMjMgMTIzLjIwMSAxMTcuMDYyQzEyMi43NzEgMTE2LjkwMSAxMjIuMzc5IDExNi42NjUgMTIyLjA1IDExNi4zNjhDMTIxLjcyIDExNi4wNyAxMjEuNDU4IDExNS43MTcgMTIxLjI4IDExNS4zMjlDMTIxLjEwMiAxMTQuOTQgMTIxLjAxIDExNC41MjQgMTIxLjAxIDExNC4xMDNDMTIxLjAxIDExMy42ODMgMTIxLjEwMiAxMTMuMjY2IDEyMS4yOCAxMTIuODc4QzEyMS40NTggMTEyLjQ5IDEyMS43MiAxMTIuMTM3IDEyMi4wNSAxMTEuODM5QzEyMi4zNzkgMTExLjU0MiAxMjIuNzcxIDExMS4zMDYgMTIzLjIwMSAxMTEuMTQ1QzEyMy42MzIgMTEwLjk4NCAxMjQuMDk0IDExMC45MDEgMTI0LjU2IDExMC45MDFDMTI1LjUwMSAxMTAuOTAxIDEyNi40MDQgMTExLjIzOSAxMjcuMDcgMTExLjgzOUMxMjcuNzM2IDExMi40NCAxMjguMTEgMTEzLjI1NCAxMjguMTEgMTE0LjEwM1oiIGZpbGw9IiM1OTY4NkYiLz4KPHBhdGggZD0iTTEyMi4zMzMgMTE4Ljk3NkMxMjIuMzMzIDExOS44MjYgMTIxLjk1OCAxMjAuNjQgMTIxLjI5MyAxMjEuMjQxQzEyMC42MjcgMTIxLjg0MSAxMTkuNzI0IDEyMi4xNzggMTE4Ljc4MiAxMjIuMTc4QzExOC4zMTYgMTIyLjE3OCAxMTcuODU1IDEyMi4wOTYgMTE3LjQyNCAxMjEuOTM1QzExNi45OTMgMTIxLjc3NCAxMTYuNjAyIDEyMS41MzggMTE2LjI3MiAxMjEuMjQxQzExNS45NDMgMTIwLjk0MyAxMTUuNjgxIDEyMC41OSAxMTUuNTAzIDEyMC4yMDJDMTE1LjMyNCAxMTkuODEzIDExNS4yMzIgMTE5LjM5NyAxMTUuMjMyIDExOC45NzZDMTE1LjIzMiAxMTguNTU2IDExNS4zMjQgMTE4LjE0IDExNS41MDMgMTE3Ljc1MUMxMTUuNjgxIDExNy4zNjMgMTE1Ljk0MyAxMTcuMDEgMTE2LjI3MiAxMTYuNzEyQzExNi42MDIgMTE2LjQxNSAxMTYuOTkzIDExNi4xNzkgMTE3LjQyNCAxMTYuMDE4QzExNy44NTUgMTE1Ljg1NyAxMTguMzE2IDExNS43NzQgMTE4Ljc4MiAxMTUuNzc0QzExOS43MjQgMTE1Ljc3NCAxMjAuNjI3IDExNi4xMTIgMTIxLjI5MyAxMTYuNzEyQzEyMS45NTggMTE3LjMxMyAxMjIuMzMzIDExOC4xMjcgMTIyLjMzMyAxMTguOTc2WiIgZmlsbD0iIzU5Njg2RiIvPgo8cGF0aCBkPSJNMTE1LjMwOCAxMjEuOTA1QzExMy43MzUgMTIxLjQyNiAxMTUuNzA3IDEyMC42OCAxMTUuNDI5IDEyMC41NzNDMTE0LjY1MyAxMjAuMjc2IDExNS43MyAxMTkuMzMzIDExNi43NCAxMTguNTM5QzExNy42MjggMTE3Ljg0MSAxMTcuNjU5IDExNy44MzkgMTE3Ljg5MiAxMTguNDY4QzExOC4wMjQgMTE4LjgyMyAxMTguMzcxIDExOS4yMDYgMTE4LjY2NSAxMTkuMzE5QzExOS4yODkgMTE5LjU1OCAxMjAuOTUxIDExOS4yNjkgMTIwLjk1MSAxMTguODM1QzEyMC45NTEgMTE4LjIyMyAxMjEuNDE1IDExOC42OTkgMTIxLjc5NCAxMTkuNTMxQzEyMi40NTcgMTIwLjk4NyAxMjIuNDM3IDEyMi40NzIgMTIxLjQ1IDEyMi40NzJDMTIwLjg1OSAxMjIuNDcyIDEyMC4zMSAxMjIuNjkxIDEyMC4xOSAxMjMuMTQ3QzExNS4wNDYgMTI0LjYgMTE2LjQ3MSAxMjMuMjg0IDExNS4zMDggMTIxLjkwNVpNMTIzLjA5NCAxMTguMDU0QzEyMS42MDkgMTE3LjQ2NiAxMjAuNTQ3IDExNC44MzggMTIwLjU0NyAxMTMuMzA5QzEyMC41NDcgMTExLjMyMyAxMjIuNTQxIDEwOS4wOTUgMTI0LjMxNyAxMDkuMDk1QzEyOC4zMTUgMTA5LjA5NSAxMzAuNjg0IDExMi4yNjEgMTI4LjgzOCAxMTguNDA5QzEyOC4zODUgMTE5LjkxOSAxMjMuOTMzIDExOC4zODcgMTIzLjA5NCAxMTguMDU0Wk0xMjUuODY2IDExNS42NDlDMTI3LjU0NCAxMTQuNDc0IDEyNS44NTcgMTExLjc1NSAxMjMuOTgxIDExMi42MUMxMjIuNDc5IDExMy4yOTQgMTIzLjExOSAxMTYuMTE1IDEyNC43NyAxMTYuMTY0QzEyNC45NjEgMTE2LjE2OSAxMjUuNDU0IDExNS45MzggMTI1Ljg2NiAxMTUuNjQ5Wk0xMjQuMzM5IDExNC41NzlDMTIzLjc3MSAxMTQuMzgxIDEyMy44MDMgMTEzLjI1NiAxMjQuMzgzIDExMy4wMzRDMTI0LjkwMiAxMTIuODM1IDEyNS42MDQgMTEzLjM5OCAxMjUuNjA0IDExNC4wMTRDMTI1LjYwNCAxMTQuNDg5IDEyNC45MzYgMTE0Ljc4NyAxMjQuMzM5IDExNC41NzlaTTExMi4zMTkgMTE2Ljk4QzEwOS45NiAxMTUuNjcgMTEwLjI4NiAxMTEuMDA2IDExMi40MTcgMTA4Ljk2NUMxMTQuMzk2IDEwNy4wNjkgMTE4Ljc3OCAxMDcuMTAzIDExOS43NjQgMTA5LjQ2MkMxMjAuNDE1IDExMS4wMjEgMTIwLjIyOCAxMTIuNiAxMTkuMzk4IDExNC4wNzdDMTE4LjE5NCAxMTYuMjE5IDExNC4yNDIgMTE4LjA0OSAxMTIuMzE5IDExNi45OFpNMTE2LjU2IDExMy41OTNDMTE3LjMyNSAxMTIuOTAxIDExNy4yOTcgMTEyLjA2IDExNi41NzIgMTExLjI1OUMxMTUuNzc3IDExMC4zNzkgMTE0LjY5MiAxMTAuMzQ1IDExNC4wMzUgMTExLjM0N0MxMTMuNTI0IDExMi4xMjcgMTEzLjQzMSAxMTIuNTI4IDExMy45NDMgMTEzLjMwOUMxMTQuNTggMTE0LjI4IDExNS42NyAxMTQuMzk5IDExNi41NiAxMTMuNTkzWk0xMTQuMzc0IDExMi4zNEMxMTQuMTI4IDExMS42OTggMTE0Ljk4NSAxMTAuOTgxIDExNS42OCAxMTEuMjQ3QzExNS45NzQgMTExLjM2IDExNi4xNjQgMTExLjcxOCAxMTYuMTAyIDExMi4wNDRDMTE1Ljk2MSAxMTIuNzg1IDExNC42MzIgMTEzLjAxMiAxMTQuMzc0IDExMi4zNFoiIGZpbGw9IiNEM0Q2REEiLz4KPHBhdGggZD0iTTExOC40NjMgMTIxLjAwOEwxMTcuOTQ1IDEyMC43OEMxMTcuNTEgMTIwLjY4NSAxMTcuMjMxIDEyMS4xMDcgMTE3LjEzNiAxMjEuNTQzTDExNi44ODEgMTIyLjcwOUMxMTYuNzg2IDEyMy4xNDUgMTE3LjA2MSAxMjMuNTcxIDExNy40OTYgMTIzLjY2NkwxMTguNDQ3IDEyMy44NzRDMTE4Ljg4MiAxMjMuOTY5IDExOS4zMTEgMTIzLjY5NCAxMTkuNDA2IDEyMy4yNTlMMTE5LjY4NSAxMjEuNTU2QzExOS43OCAxMjEuMTIgMTE5LjQ3OSAxMjEuMjI4IDExOS4wNDMgMTIxLjEzM0wxMTguNjI4IDEyMS4wNDNDMTE4LjU3MyAxMjEuMzIxIDExOC41MTYgMTIxLjYxMyAxMTguNDcxIDEyMS44MzNDMTE4LjQzOCAxMjEuOTk4IDExOC40MDcgMTIyLjE0OCAxMTguMzc5IDEyMi4yODJDMTE4LjM1MSAxMjIuNDE1IDExOC4zMjcgMTIyLjUzMyAxMTguMzA1IDEyMi42MzVDMTE4LjI4MiAxMjIuNzM4IDExOC4yNjIgMTIyLjgyNSAxMTguMjQ1IDEyMi44OTdDMTE4LjIyOCAxMjIuOTY5IDExOC4yMTQgMTIzLjAyNSAxMTguMjAyIDEyMy4wNjhDMTE4LjE5NiAxMjMuMDg5IDExOC4xOTMgMTIzLjEwNyAxMTguMTg4IDEyMy4xMjFDMTE4LjE4MyAxMjMuMTM1IDExOC4xNzggMTIzLjE0NSAxMTguMTc1IDEyMy4xNTJDMTE4LjE3MyAxMjMuMTU1IDExOC4xNzIgMTIzLjE1NiAxMTguMTcxIDEyMy4xNThDMTE4LjE3IDEyMy4xNTkgMTE4LjE2OCAxMjMuMTYgMTE4LjE2NyAxMjMuMTZMMTE4LjE2NSAxMjMuMTU4QzExOC4xNjQgMTIzLjE1NiAxMTguMTYzIDEyMy4xNTIgMTE4LjE2MyAxMjMuMTQ4QzExOC4xNjIgMTIzLjE0IDExOC4xNjIgMTIzLjEyOSAxMTguMTYzIDEyMy4xMTVDMTE4LjE2MyAxMjMuMSAxMTguMTYzIDEyMy4wODMgMTE4LjE2NSAxMjMuMDYxQzExOC4xNjggMTIzLjAxOCAxMTguMTc1IDEyMi45NjEgMTE4LjE4MyAxMjIuODkxQzExOC4xOTIgMTIyLjgyIDExOC4yMDMgMTIyLjczNiAxMTguMjE2IDEyMi42NEMxMTguMjI5IDEyMi41NDMgMTE4LjI0NiAxMjIuNDMxIDExOC4yNjQgMTIyLjMwOEMxMTguMjgxIDEyMi4xODUgMTE4LjMwMSAxMjIuMDUxIDExOC4zMjMgMTIxLjkwM0MxMTguMzQ2IDEyMS43NTUgMTE4LjM3IDEyMS41OTIgMTE4LjM5NyAxMjEuNDE5QzExOC40MTYgMTIxLjI5OSAxMTguNDQyIDEyMS4xNCAxMTguNDYzIDEyMS4wMDhaIiBmaWxsPSIjOThDNkQ4Ii8+CjxwYXRoIGQ9Ik0xMDMuNTgxIDExNi4zNjdDMTAzLjQ3OSAxMTYuMTAzIDEwMy42MjIgMTE1LjY5OSAxMDMuODk4IDExNS40NjlDMTA0LjMwMyAxMTUuMTMzIDEwNC41MDQgMTE1LjE1NSAxMDQuOTMgMTE1LjU4MUMxMDUuMjIgMTE1Ljg3MSAxMDUuMzU1IDExNi4yNzYgMTA1LjIzIDExNi40NzlDMTA0LjkwMiAxMTcuMDA5IDEwMy43OTggMTE2LjkzNCAxMDMuNTgxIDExNi4zNjdaIiBmaWxsPSIjOThDNkQ4Ii8+CjxwYXRoIGQ9Ik0xMDYuMDM4IDExMi4yODFDMTA1LjY4IDExMS44NDkgMTA1LjcwMiAxMTEuNjYgMTA2LjE2NSAxMTEuMTk3QzEwNi42ODkgMTEwLjY3NCAxMDYuNzY0IDExMC42NzQgMTA3LjI4NyAxMTEuMTk3QzEwNy43NTEgMTExLjY2IDEwNy43NzMgMTExLjg0OSAxMDcuNDE1IDExMi4yODFDMTA3LjE3NiAxMTIuNTY4IDEwNi44NjYgMTEyLjgwMyAxMDYuNzI2IDExMi44MDNDMTA2LjU4NiAxMTIuODAzIDEwNi4yNzcgMTEyLjU2OCAxMDYuMDM4IDExMi4yODFaIiBmaWxsPSIjOThDNkQ4Ii8+CjxwYXRoIGQ9Ik0xMDYuMDM4IDEwNy4yMjRDMTA1LjY4MiAxMDYuNzk1IDEwNS42OTQgMTA2LjYxMiAxMDYuMTA2IDEwNi4yMDFDMTA2LjY1IDEwNS42NTcgMTA3LjczOCAxMDUuODggMTA3LjczOCAxMDYuNTM2QzEwNy43MzggMTA2Ljk3IDEwNy4wNzIgMTA3Ljc0NyAxMDYuNyAxMDcuNzQ3QzEwNi41NzUgMTA3Ljc0NyAxMDYuMjc3IDEwNy41MTIgMTA2LjAzOCAxMDcuMjI0WiIgZmlsbD0iIzk4QzZEOCIvPgo8cGF0aCBkPSJNMTE3Ljk1NSAxMzYuNTQxQzExNC41NzggMTM2LjExMSAxMTAuMDg3IDEzNC44ODEgMTA5LjQxIDEzNC4yNzRDMTA4LjEyMyAxMzMuMTE5IDEwOC45NDIgMTI3Ljg3OSAxMTAuNDY2IDEyNi41NEMxMTEuMzg3IDEyNS43MzEgMTE1LjAzOSAxMjQuNjYzIDExNy4xODIgMTI1LjE2OUMxMTkuNjQyIDEyNS43NDkgMTIzLjkzMiAxMjguNjQ4IDEyNC4yNSAxMjkuNTA4QzEyNC42MjUgMTMwLjUyMiAxMjQuMDQ4IDEzNC41NDEgMTIzLjMyMyAxMzUuNTM2QzEyMi42OTQgMTM2LjM5OSAxMjAuMzI1IDEzNi44NDMgMTE3Ljk1NSAxMzYuNTQxWk0xMjAuNTQgMTM0LjA4NEMxMjEuMjk3IDEzMy4zOTkgMTIxLjM0MiAxMzEuODQyIDEyMC42MjcgMTMxLjEyNkMxMTkuNDkgMTI5Ljk4OSAxMTcuMTEyIDEzMC44NjkgMTE3LjExMiAxMzIuNDI4QzExNy4xMTIgMTM0LjMwNCAxMTkuMTg5IDEzNS4zMDcgMTIwLjU0IDEzNC4wODRaTTExOC41ODIgMTMzLjEyNUMxMTguMzExIDEzMi40MTkgMTE4LjY4IDEzMS42MDggMTE5LjI3MiAxMzEuNjA4QzEyMCAxMzEuNjA4IDEyMC4yMjggMTMyLjE3MyAxMTkuODEyIDEzMi45NDlDMTE5LjM4MSAxMzMuNzU1IDExOC44NTMgMTMzLjgzIDExOC41ODIgMTMzLjEyNVpNMTE2LjQyMiAxMzMuMTQzQzExNy4wOTQgMTMyLjMzMyAxMTYuNzExIDEzMS4xNzYgMTE1LjY4MSAxMzAuOTAyQzExNC41ODEgMTMwLjYxIDExNC4wNTIgMTMxLjE4MyAxMTQuODY5IDEzMS43OEMxMTUuNjgzIDEzMi4zNzUgMTE1LjU1MSAxMzIuNjc5IDExNC41MzMgMTMyLjU1N0MxMTMuMzI3IDEzMi40MTMgMTEyLjgzMyAxMzEuNTY2IDExMy4yNTQgMTMwLjM2MkMxMTMuNjgxIDEyOS4xNDIgMTE1LjE5NyAxMjguODYzIDExNS43NTMgMTI5LjkwMkMxMTYuMTkzIDEzMC43MjUgMTE2LjYyNyAxMzAuNzkzIDExNi45IDEzMC4wODNDMTE3LjIyOSAxMjkuMjI0IDExNS45MTQgMTI4LjIzNyAxMTQuNDQgMTI4LjIzN0MxMTEuOTkzIDEyOC4yMzcgMTEwLjgxNiAxMzEuMDc0IDExMi41NDYgMTMyLjgwM0MxMTMuNSAxMzMuNzU4IDExNS43NDkgMTMzLjk1NSAxMTYuNDIyIDEzMy4xNDNaIiBmaWxsPSIjN0JBOUI5Ii8+CjxwYXRoIGQ9Ik0xMjAuMTE4IDEzOC41OTJDMTE4Ljc1MSAxMzcuMjk3IDEyMS45MTYgMTM2LjA5NiAxMjMuNjA2IDEzOC4yODNDMTI0LjQ2OSAxMzkuMzIyIDEyMi44NTMgMTM5Ljk3NiAxMjAuMTE4IDEzOC41OTJaTTEwNi4xMiAxMzUuNjU4QzEwNC43NTEgMTM0LjI4OSAxMDYuOTYzIDEzMy45MDQgMTA4LjU4MSAxMzUuMjNMMTA5Ljk0MSAxMzUuOTJMMTA4LjA1OSAxMzYuMDYxQzEwNy4yMTUgMTM2LjA2MiAxMDYuMzQzIDEzNS44ODEgMTA2LjEyIDEzNS42NThWMTM1LjY1OFpNMTI1LjM2MyAxMjcuODY2QzEyNS4wODIgMTI3LjQxIDEyOC4zMzMgMTI2LjI2NyAxMjguNjg4IDEyNi40ODZDMTI4Ljg0NiAxMjYuNTg0IDEyOC45NzUgMTI3LjMzNyAxMjguOTc1IDEyOC4xNjFDMTI4Ljk3NSAxMjkuMjM5IDEyOC44NCAxMjkuNjU4IDEyOC40OSAxMjkuNjU4QzEyOC4yMjIgMTI5LjY1OCAxMjUuNDc2IDEyOC4wNDcgMTI1LjM2MyAxMjcuODY2Wk0xMTguNjQxIDEyMS4wOTVDMTE0Ljg3MSAxMjAuNzUzIDExNS4zNzggMTE5LjMyNyAxMTYuMzYxIDExOC40OTlDMTE2LjkyOCAxMTguMDIxIDExNy4zNCAxMTcuNzc5IDExNy45MTMgMTE3LjY2N0MxMTcuOTEzIDExNy42NjcgMTE3Ljg3MiAxMTkuMDQ2IDExOC42NjYgMTE5LjMxOUMxMTkuMjk3IDExOS41MzYgMTIwLjI3OCAxMTkuMDY0IDEyMC40NzEgMTE4LjY3NUMxMjIuMTg4IDExNS4yMDEgMTIxLjQxNSAxMTguNjk5IDEyMS43OTQgMTE5LjUzMkMxMjMuMjM4IDEyNC4wMDcgMTIwLjE4OCAxMjEuNzMgMTE4LjY0MSAxMjEuMDk1Wk0xMDQuOTEyIDEyMS4yM0MxMDQuMjQyIDEyMC40ODcgMTAzLjY5MyAxMTkuNzI5IDEwMy42OTMgMTE5LjU0NEMxMDMuNjkzIDExOS4xMTYgMTA0Ljc0NiAxMTkuMTEyIDEwNS44NjMgMTE5LjUzN0MxMDYuNTkzIDExOS44MTQgMTEwLjM0NyAxMjAuMDA2IDExMC4zNDcgMTIxLjE1M0MxMTAuMzQ3IDEyMS44OTkgMTA4Ljg5IDEyMy43NjIgMTA4LjcyNiAxMjMuNzYyQzEwOC41NjMgMTIzLjc2MiAxMDUuNTgzIDEyMS45NzIgMTA0LjkxMiAxMjEuMjNaIiBmaWxsPSIjQTZCM0MyIi8+CjxwYXRoIGQ9Ik0xMTguNDYzIDEyMS4wMDhDMTE4LjQ0MiAxMjEuMTQgMTE4LjQxNiAxMjEuMjk5IDExOC4zOTcgMTIxLjQxOUMxMTguMzcgMTIxLjU5MiAxMTguMzQ1IDEyMS43NTUgMTE4LjMyMyAxMjEuOTAzQzExOC4zIDEyMi4wNTEgMTE4LjI4MSAxMjIuMTg1IDExOC4yNjMgMTIyLjMwOEMxMTguMjQ1IDEyMi40MzEgMTE4LjIyOSAxMjIuNTQyIDExOC4yMTYgMTIyLjYzOUMxMTguMjAzIDEyMi43MzYgMTE4LjE5MSAxMjIuODIgMTE4LjE4MyAxMjIuODlDMTE4LjE3NSAxMjIuOTYxIDExOC4xNjggMTIzLjAxOCAxMTguMTY1IDEyMy4wNjFDMTE4LjE2MyAxMjMuMDgzIDExOC4xNjQgMTIzLjEgMTE4LjE2MyAxMjMuMTE1QzExOC4xNjIgMTIzLjEyOSAxMTguMTYyIDEyMy4xNCAxMTguMTYzIDEyMy4xNDhDMTE4LjE2MyAxMjMuMTUyIDExOC4xNjQgMTIzLjE1NiAxMTguMTY1IDEyMy4xNThMMTE4LjE2NyAxMjMuMTZDMTE4LjE2OCAxMjMuMTYgMTE4LjE3IDEyMy4xNTkgMTE4LjE3MSAxMjMuMTU4QzExOC4xNzIgMTIzLjE1NyAxMTguMTczIDEyMy4xNTUgMTE4LjE3NSAxMjMuMTUyQzExOC4xNzggMTIzLjE0NSAxMTguMTgyIDEyMy4xMzUgMTE4LjE4NyAxMjMuMTIxQzExOC4xOTIgMTIzLjEwNyAxMTguMTk2IDEyMy4wODkgMTE4LjIwMiAxMjMuMDY3QzExOC4yMTMgMTIzLjAyNSAxMTguMjI4IDEyMi45NjkgMTE4LjI0NSAxMjIuODk3QzExOC4yNjIgMTIyLjgyNSAxMTguMjgyIDEyMi43MzggMTE4LjMwNSAxMjIuNjM1QzExOC4zMjcgMTIyLjUzMyAxMTguMzUxIDEyMi40MTUgMTE4LjM3OSAxMjIuMjgxQzExOC40MDYgMTIyLjE0OCAxMTguNDM4IDEyMS45OTggMTE4LjQ3MSAxMjEuODMzQzExOC41MTYgMTIxLjYxMyAxMTguNTczIDEyMS4zMjEgMTE4LjYyOCAxMjEuMDQzTDExOC40NjMgMTIxLjAwOFoiIGZpbGw9IiM0QzlDQkIiLz4KPHBhdGggZD0iTTMwLjk5ODYgMTQyLjY3M0MyNi40NDg5IDE0MC45MTYgMjEuNDY2NCAxMzYuODA1IDE4Ljc5MjEgMTMyLjYwM0MxNS40MDUzIDEyNy4yODEgMTQuNDA1MiAxMjMuNDE5IDE1LjA2MzYgMTE4LjIwM0MxNS42ODA2IDExMy4zMTUgMTcuMzU1OCAxMTAuNTU3IDIyLjIyMzIgMTA2LjQxM0MyOS4wNDE3IDEwMC42MDggMzQuNDg0NiA5Ny45Nzk5IDQwLjU5NDcgOTcuNTQxNUM0OS44MjEyIDk2Ljg3OTQgNTUuMDk3OCA5OS44MTA5IDU3LjY0NjUgMTA3LjAxNUM1OC41MDQzIDEwOS40MzkgNTguNjg1NiAxMTUuNTcgNTguMDU0NCAxMjAuODA5QzU3LjgzMTEgMTIyLjY2MyA1Ny40Nzg5IDEyNi4wNTUgNTcuMjcxOCAxMjguMzQ4QzU2LjU0NjcgMTM2LjM3NSA1NC40MTQ3IDE0MC40MTcgNDkuNjI1MyAxNDIuODQzQzQ4LjI5NzkgMTQzLjUxNiA0Ny43MTk5IDE0My41NjEgNDAuNjkyNCAxNDMuNTM5QzMzLjUxNTEgMTQzLjUxNiAzMy4wODEgMTQzLjQ3OCAzMC45OTg2IDE0Mi42NzNaTTAuMDc5NTc2NSAxMDQuOTY1QzAuMDgxNTcyMyAxMDMuODUzIDAuMTQ0NDAzIDEwMy40MzggMC4yMTk0MjcgMTA0LjA0NEMwLjI5NDQ1MSAxMDQuNjUgMC4yOTI4OTkgMTA1LjU2IDAuMjE1OTk2IDEwNi4wNjdDMC4xMzkwNzkgMTA2LjU3MyAwLjA3NzY4MzggMTA2LjA3OCAwLjA3OTU3NjUgMTA0Ljk2NVpNMC4wMDE3MzM1MSAxMDIuMjRDMC4wMTc4OTkyIDEwMS44NDggMC4wOTc3ODk3IDEwMS43NjggMC4yMDUzOTggMTAyLjAzN0MwLjMwMjc3MiAxMDIuMjggMC4yOTA3OTcgMTAyLjU3MSAwLjE3ODc4NyAxMDIuNjgzQzAuMDY2Nzc0OCAxMDIuNzk1IC0wLjAxMjkwMjEgMTAyLjU5NiAwLjAwMTczMzUxIDEwMi4yNFpNMC4wMjgzNDQ0IDc1LjEzMjZDMC4wMjgzNDQ0IDc0LjY2OTEgMC4xMDQ4NTcgNzQuNDc5NSAwLjE5ODM2OSA3NC43MTEyQzAuMjkxODg0IDc0Ljk0MyAwLjI5MTg4NCA3NS4zMjIyIDAuMTk4MzY5IDc1LjU1MzlDMC4xMDQ4NTcgNzUuNzg1NyAwLjAyODM0NDQgNzUuNTk2MSAwLjAyODM0NDQgNzUuMTMyNloiIGZpbGw9InVybCgjcGFpbnQyX3JhZGlhbF82NzlfMTkxMCkiLz4KPHBhdGggZD0iTTM1LjMxMzMgMTEyLjU0NEwzNy45IDExMS4wOTFMMzkuMzUxNyAxMjEuNDZMMzkuMjQ4IDEyOC45MjZMMzUuMzcwMyAxMzAuODUxTDMyLjkyMjkgMTI2LjQzN0wzNS4zMTMzIDExMi41NDRaIiBmaWxsPSIjMzA2MEFEIi8+CjxwYXRoIGQ9Ik0zOC43Mjk1IDExNi42OUwzOS4zNTE2IDEyNC41NzFMNDMuMjkxOSAxMjUuOTE5TDQzLjM5NTYgMTA5LjIyNUw0MC4xODEyIDExMC4wNTRMMzguNzI5NSAxMTYuNjlaIiBmaWxsPSIjNjA2MzY4Ii8+CjxwYXRoIGQ9Ik0zNS4zNjk5IDEzMC44NTFDMzMuMDMyNCAxMjkuNzMgMjkuNzMwMSAxMjYuNDA4IDI4LjY2MjEgMTI0LjEwM0MyOC4wMzMyIDEyMi43NDUgMjcuNjM5NyAxMjIuMzEgMjYuODkyMyAxMjIuMTQ2QzI1Ljc4NzkgMTIxLjkwMyAyNS44MDg0IDEyMS45NzEgMjYuMzIxOCAxMjAuMjU4QzI2LjU5MzYgMTE5LjM1IDI2LjkwMzIgMTE4Ljk1NCAyNy4zNDA2IDExOC45NTRDMjcuNjg2MiAxMTguOTU0IDI5LjE1OTcgMTE3Ljg0MSAzMC42MTQ5IDExNi40OEMzMi4wNzAyIDExNS4xMTkgMzMuNzIyNiAxMTMuNjc3IDM0LjI4NjkgMTEzLjI3NUwzNS4zMTI5IDExMi41NDRMMzUuNjc2NyAxMTQuMjQxQzM3LjA0NDMgMTIwLjYxOCAzNy4wMjcgMTI0LjI2NiAzNS42MTk4IDEyNi4yNDNDMzQuNTUyMSAxMjcuNzQ0IDM0Ljg0MTcgMTI4LjM5MyAzNi41NzkxIDEyOC4zOTNDMzcuMzk3IDEyOC4zOTMgMzcuODIyMiAxMjguMTg1IDM4LjEzMDUgMTI3LjYzNEMzOC42NzQ4IDEyNi42NjMgMzguNjMxNSAxMjAuOTY4IDM4LjA0NTQgMTE2LjQyNkMzNy4zNDQ2IDExMC45OTUgMzcuMzEyNyAxMTEuMTY3IDM5LjE1NzggMTEwLjM5OEM0MC4wMzYgMTEwLjAzMSA0MC44MTggMTA5Ljc5NyA0MC44OTU1IDEwOS44NzdDNDAuOTczMSAxMDkuOTU2IDQwLjgxOTEgMTExLjIzNSA0MC41NTM0IDExMi43MThDMzkuNjA2NyAxMTguMDAzIDM5LjkzMTUgMTIzLjIzOCA0MS4yNzUyIDEyNC4zNTNDNDEuNjg1MSAxMjQuNjkzIDQxLjcyMzMgMTIzLjk2OSA0MS41NDUyIDExOS4yMzJDNDEuMzE1NiAxMTMuMTIzIDQxLjUxNzQgMTEwLjkxOSA0Mi40MDc1IDEwOS44MkM0Mi45MTk4IDEwOS4xODggNDMuMjEzIDEwOS4xMTYgNDQuNDk1OSAxMDkuMzFDNDYuOTU2NiAxMDkuNjgyIDQ2Ljk1MDYgMTA5LjY2OCA0Ni4yMDk5IDExMy4yNTRDNDUuODQ5NiAxMTQuOTk5IDQ1LjQ0ODMgMTE3LjIyNCA0NS4zMTggMTE4LjJMNDUuMDgxMiAxMTkuOTczTDQ1Ljk0NDYgMTE5LjY0NUM0Ni40MTk1IDExOS40NjUgNDcuNDQxMyAxMTguOTk0IDQ4LjIxNTMgMTE4LjU5OUw0OS42MjI2IDExNy44ODFMNDkuMzg5MyAxMTguNjdDNDkuMjYxIDExOS4xMDUgNDkuMDY3MiAxMjAuNzAxIDQ4Ljk1ODcgMTIyLjIxOEw0OC43NjE0IDEyNC45NzZMNDcuMTA5NSAxMjQuMzI1QzQ2LjIwMSAxMjMuOTY3IDQ1LjMxMTYgMTIzLjY3NCA0NS4xMzMxIDEyMy42NzRDNDQuOTQxIDEyMy42NzQgNDQuODA4NSAxMjUuNTMyIDQ0LjgwODUgMTI4LjIyNEM0NC44MDg1IDEzMS44OTEgNDQuNzE3MSAxMzIuNzc1IDQ0LjMzODYgMTMyLjc3NUM0MS4wMTk4IDEzMi4yOTUgMzguNzg0MiAxMzIuNDU4IDM1LjM2OTkgMTMwLjg1MVoiIGZpbGw9IiNFRUMyM0IiLz4KPHBhdGggZD0iTTQxLjE2MzkgMTMyLjQxOEM0MS4xNjM5IDEzMi4yODMgNDEuMzQwOCAxMzAuNTE1IDQxLjY2OTIgMTI4LjE0NUM0MS45OTc3IDEyNS43NzUgNDIuMzU3NyAxMjIuOTYxIDQyLjQ2OTIgMTIxLjg5M0M0Mi41ODA4IDEyMC44MjQgNDIuNzk2OSAxMjAuMDI3IDQyLjk0OTUgMTIwLjEyMUM0My42NDQzIDEyMC41NTEgNDQuMjkzOCAxMjkuMzc1IDQ0LjA0NzQgMTMxLjQyQzQzLjg5MDkgMTMyLjcxOSA0My42NTk1IDEzMi43MzcgNDMuMzAxOSAxMzIuNjc3QzQyLjY2MDIgMTMyLjU3IDQxLjkxMDEgMTMyLjUxMyA0MS4xNjM5IDEzMi40MThaTTMyLjU5NjkgMTIyLjczNUMzMS42ODEzIDEyMi41ODMgMzAuODI0MiAxMjEuNDQyIDMwLjgyNDIgMTIwLjM3NEMzMC44MjQyIDExOS40MDcgMzIuMjM4MSAxMTcuOTM3IDMzLjE2ODUgMTE3LjkzN0MzNC42NjY2IDExNy45MzcgMzUuODM5MiAxMjAuNTEzIDM0Ljk0ODQgMTIxLjg0N0MzNC41NTE3IDEyMi40NDEgMzMuNDE0NyAxMjIuODcgMzIuNTk2OSAxMjIuNzM1WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTExNC4wMjEgMTM3LjQ5OEMxMDcuMzE4IDEzNS43NCAxMDYuMjczIDEzNC4xMDQgMTA2LjYwOSAxMjUuODkxQzEwNi43MTYgMTIzLjI4MSAxMDYuODg1IDEyMC41MzkgMTA2Ljk4NiAxMTkuNzk3QzEwNy41NDUgMTE1LjY2NCAxMDguMzQyIDExMi4xNzMgMTA5LjA4NiAxMTAuNTk5QzEwOS43NDQgMTA5LjIwNiAxMDkuODQyIDEwOC43MTIgMTA5LjUzNSAxMDguMzQyQzEwOC40NSAxMDcuMDM1IDExMC4zNzEgMTA0Ljk5NSAxMTEuOTEgMTA1LjgxOUMxMTIuNDU3IDEwNi4xMTEgMTEyLjkxOCAxMDYuMDc3IDExMy45NDUgMTA1LjY2NUMxMTcuMDYyIDEwNC40MTkgMTIzLjExNyAxMDUuNTcxIDEyNi44MTQgMTA4LjExNUMxMjguMTQ5IDEwOS4wMzMgMTI4LjQ4NSAxMDkuMTM2IDEyOS4wMzggMTA4Ljc5MUMxMzAuNTIzIDEwNy44NjMgMTMyLjQ1MSAxMTAuNTczIDEzMS4yNSAxMTEuOUMxMzAuNzg3IDExMi40MTIgMTMwLjc1NSAxMTIuNzA1IDEzMS4wNjUgMTEzLjU5NkMxMzEuODUgMTE1Ljg0NiAxMzEuNzk0IDExNi4xMDQgMTI4LjAwNiAxMjcuNzE5QzEyNS41OCAxMzUuMTYgMTI0LjU0MyAxMzcuMzY1IDEyMy4yNjQgMTM3LjgxMUMxMjEuNjUzIDEzOC4zNzMgMTE2LjcyMyAxMzguMjA2IDExNC4wMjEgMTM3LjQ5OFpNMTIyLjU0MSAxMzUuMjI5QzEyMy4wMDMgMTM0LjU0MSAxMjMuMzU5IDEzMy4zMTMgMTIzLjUxMyAxMzEuODc4QzEyMy43OTQgMTI5LjI1IDEyMy44MTkgMTI5LjI4MyAxMjAuMzE3IDEyNy42MDFDMTE3LjY4NSAxMjYuMzM4IDExMy44MzMgMTI1Ljk3MiAxMTEuODU1IDEyNi43OThDMTEwLjgyOSAxMjcuMjI3IDExMC41OTUgMTI3LjU1NiAxMTAuMDU5IDEyOS4zMjdDMTA5LjExNiAxMzIuNDQzIDEwOS43MDMgMTM0LjMwOCAxMTEuODMyIDEzNC45NThDMTEzLjEzOSAxMzUuMzU3IDEyMC4wOSAxMzYuNDMgMTIwLjk4IDEzNi4zN0MxMjEuNTA0IDEzNi4zMzUgMTIyLjA4MiAxMzUuOTEyIDEyMi41NDEgMTM1LjIyOVpNMTE5LjQ1MSAxMjIuNDJDMTE5LjYzIDEyMi4wODEgMTE5LjY4NSAxMjEuNTU2IDExOS42ODUgMTIxLjU1NkMxMjAuMTU2IDEyMS42NTQgMTIwLjQ4IDEyMS42ODMgMTIwLjcyNiAxMjEuNzE5QzEyMS4xMDUgMTIxLjc3NCAxMjEuNTY0IDEyMS41ODcgMTIxLjc0NSAxMjEuMzAzQzEyMi4xNDkgMTIwLjY3MSAxMjAuNDc0IDExNy43MDEgMTE5LjU0OCAxMTcuNDA4QzExOC42NjggMTE3LjEyOCAxMTUuOTM1IDExOC44MjcgMTE1LjkzNSAxMTkuNjUzQzExNS45MzUgMTIwLjAyMiAxMTYuNTUzIDEyMC40MTEgMTE2LjgzMSAxMjAuNTE4QzExNi45ODIgMTIwLjU3NiAxMTcuMjUyIDEyMC43MzggMTE3LjU3NiAxMjAuOTM4QzExNy4zOCAxMjEuMzUxIDExNy4zNDYgMTIxLjQ3OCAxMTcuMzEyIDEyMS43MjRDMTE3LjIwNCAxMjIuNDk4IDExNy40MzUgMTIyLjY4NiAxMTguNDE2IDEyMi45MTJDMTE5LjE1MSAxMjIuOTkxIDExOS4yNSAxMjIuODQ1IDExOS40NTEgMTIyLjQyWk0xMjcuMjY1IDExNi44MzZDMTMwLjkzMyAxMTMuNDc1IDEyNy4wNzQgMTA3LjQxIDEyMi43MDUgMTA5LjY3QzEyMS42MjYgMTEwLjIyOCAxMjAuNjU0IDExMi4wMTkgMTIwLjY1NCAxMTMuNDQ5QzEyMC42NTQgMTE1LjY2NCAxMjIuODMxIDExNy45MzcgMTI0Ljk1NyAxMTcuOTQxQzEyNS43MzMgMTE3Ljk0MyAxMjYuNDE0IDExNy42MTcgMTI3LjI2NSAxMTYuODM2Wk0xMTcuMTQxIDExNS42MjJDMTE5LjAzNSAxMTQuNDY4IDExOS44MDQgMTExLjc5NSAxMTguODAyIDEwOS44NTdDMTE4LjA4MiAxMDguNDY0IDExNy4wMDggMTA3LjgzIDExNS4zNjYgMTA3LjgzQzExMS41MDggMTA3LjgzIDEwOS40ODUgMTEzLjIzNCAxMTIuNDY1IDExNS41NzhDMTEzLjU3IDExNi40NDggMTE1Ljc1NCAxMTYuNDY4IDExNy4xNDEgMTE1LjYyMloiIGZpbGw9IiMwOTk2RDEiLz4KPHBhdGggb3BhY2l0eT0iMC45IiBkPSJNMzMuMTc1NyAyNkwzMi4zODEgMjcuMjE5QzMxLjk0NDkgMjcuODg5IDMxLjI4MzYgMjkuMzI0OCAzMC45MDkxIDMwLjQwODhDMzAuNTM0NyAzMS40OTI4IDI5Ljk5MjIgMzIuOTcwMSAyOS43MDQzIDMzLjY5MjhDMjkuNDE2MyAzNC40MTU0IDI5LjE3MzMgMzYuMTE1IDI5LjE2MzggMzcuNDdDMjkuMTQzNiA0MC4zNTMgMjguNzQ5NCA0MS44ODg4IDI3LjYzNjQgNDMuNDIyNUMyNi45MjEyIDQ0LjQwOCAyNi42NzUxIDQ0LjUyMjMgMjUuNDUzMiA0NC40NDFDMjMuODk5NSA0NC4zMzc3IDIyLjgxNTMgNDUuMDgxNSAyMS4yMTcgNDcuMzQ4MUMyMC43MTY1IDQ4LjA1OCAyMC4xMzY3IDQ4LjYzOTMgMTkuOTI4OSA0OC42MzkzQzE5LjcyMTEgNDguNjM5MyAxOS4xODA1IDQ4LjIzMTkgMTguNzI2MiA0Ny43MzUxQzE4LjI3MTkgNDcuMjM4MiAxNy45OTQ0IDQ3LjAxMTUgMTguMTExIDQ3LjIyOThDMTguNDkgNDcuOTM5NiAxNy41OTA0IDUzLjI2MTUgMTYuODM3OCA1NC43NjQyQzE1Ljk2NyA1Ni41MDI4IDE0LjM4NzIgNTcuNzIzMyAxMi41MzU0IDU4LjA4NDNDMTEuMDA1OSA1OC4zODI2IDExLjIyODMgNTguMDkgOC43MTc5NSA2My4wOTQ2QzYuNjYzMzQgNjcuMTkwNyA1Ljk2OTM1IDY3LjgyNjQgMi42NTUzMyA2OC42NTQyQzEuNDk3MTcgNjguOTQzNCAwLjQ1ODc1OSA2OS43MTQyIDAgNzAuNTI4N1Y3Mi44MjYzQzAuMzU2MTkzIDcyLjc0NzMgMC44Mjg3MjggNzIuNjIwMyAxLjYxNDk5IDcyLjM4NzNDMi45OTQ4NiA3MS45NzgyIDQuODIzNDggNzEuNDgwNiA1LjY3ODEgNzEuMjgwNkM3LjcyOTM0IDcwLjgwMDYgOS4zOTM1MSA2OS4yMTk1IDkuOTIyNzkgNjcuMjQ2N0MxMC4yNDI0IDY2LjA1NTQgMTAuNzAzMyA2NS40MjQxIDExLjk5OTIgNjQuNDA1OEMxMy4yODQgNjMuMzk2MiAxMy43NTY1IDYyLjc1NDYgMTQuMDY3MSA2MS41OTY5QzE0LjM3MzkgNjAuNDUzMiAxNC43MDExIDYwLjAwNCAxNS40NDA3IDU5LjcwNjNDMTcuMDE1MyA1OS4wNzI0IDIwLjY0NDQgNTUuODY1NSAyMS4yMzYzIDU0LjU4NThDMjEuNTM3MyA1My45MzQ5IDIyLjExOTggNTIuNTM5OSAyMi41Mjg3IDUxLjQ4NjJDMjMuMjYzNCA0OS41OTMyIDI1LjAxOTUgNDcuMzI2MSAyNS43NTAxIDQ3LjMyNjFDMjUuOTU3NCA0Ny4zMjYxIDI3LjEwOSA0Ni43MjQ4IDI4LjMwOTMgNDUuOTkwOEMzMS4yMzc2IDQ0LjIwMDMgMzEuNTk5MSA0My40MTM2IDMxLjU5OTEgMzguODYxNEMzMS41OTkxIDM1LjY4MTQgMzEuNjg3IDM1LjE1NTcgMzIuMzQ2OCAzNC4zNjg0QzMzLjAwMzcgMzMuNTg0NyAzMy4wOTk3IDMzLjAyNDkgMzMuMTM1MSAyOS43MzkxTDMzLjE3NTcgMjZaTTI0LjMzMzggNjMuNTk1OEMyNC4yMDM3IDYzLjYxMzUgMjQuMDczOCA2My42NjE4IDIzLjkyNTggNjMuNzM2MkMyMi45Mzk3IDY0LjIzMTUgMjIuNjEzNiA2NC45MTAyIDIxLjU5NzMgNjguNTg2QzIxLjAxNzkgNzAuNjgxNSAyMC40MjEyIDcyLjA4MTkgMTkuODc5OCA3Mi42MTc4QzE5LjQyMzUgNzMuMDY5NSAxOC45MzgzIDczLjg4MjMgMTguODAxIDc0LjQyNDJDMTguMTgxIDc2Ljg3MTUgMTYuNzQ1NCA3OC42MDczIDE0LjQ0OTUgNzkuNjg3MUMxMi40NTU1IDgwLjYyNDkgMTAuNjQwNyA4Mi4xMTc5IDkuMTExMDIgODQuMDc5OEM4LjMyMTA5IDg1LjA5MjkgNy40ODc1MiA4Ni4wMjkgNy4yNTY3OCA4Ni4xNjA5QzYuNzYxNjQgODYuNDQ0IDUuNzA4OTkgODguODk0MSA0LjYwNzg1IDkyLjMyOEMzLjcxMDkyIDk1LjEyNSAxLjcxMDY1IDk3LjQ2ODIgMC40Nzg1MTQgOTcuMTY1OEMwLjI1NDkzMSA5Ny4xMTA5IDAuMTA2OTExIDk3LjExMzcgMCA5Ny4yOTAxVjEwNC40OTJDMC41NTY0MTEgMTAzLjY2NCAxLjIzNDIgMTAyLjYyOSAyLjU2MzQ3IDEwMC41NEM0LjM0NTExIDk3LjczOTcgNi40NjM4NSA5NC40ODY3IDcuMjcxNzMgOTMuMzEyNEM4LjA3OTYgOTIuMTM4MSA5LjM1Njg3IDkwLjE0ODggMTAuMTA4NiA4OC44OTE2QzExLjIzNDIgODcuMDA5MiAxMi4xMDE2IDg2LjEyNDMgMTUuMDI4NCA4My44ODMzTDE4LjU4MzEgODEuMTYyN0wyMC40MDUzIDc1Ljk4ODFDMjMuMTg5NSA2OC4wODE3IDIzLjk1ODUgNjcuMTk5NyAyNy43NjI0IDY3LjU1MTVDMjkuNDYwNSA2Ny43MDg1IDMwLjYzMzQgNjcuNjE5NiAzMi4wNzM0IDY3LjIyNjdDMzQuMTgyNiA2Ni42NTEyIDM0LjQ3OTkgNjYuMTg3MiAzMy4zNTUxIDY1LjIzMThDMzIuNjU1NCA2NC42Mzc2IDMxLjMxNTMgNjQuNjQgMjguMjM4OCA2NS4yNDE4QzI3LjMzOTcgNjUuNDE3NyAyNi45MzQzIDY1LjI3NTcgMjUuODkzMiA2NC40MTc4QzI1LjEwNTEgNjMuNzY4NCAyNC43MjQxIDYzLjU0MjYgMjQuMzMzOCA2My41OTU4Wk0zMS43MTY2IDgzLjI1OThDMzEuNjYwNSA4My4yNzQ5IDMxLjU5MjEgODMuMzEyNiAzMS41MTE1IDgzLjM3MjFDMzEuMjcwOSA4My41NSAzMC42NTM2IDgzLjc5IDMwLjEzNzkgODMuOTA1NEMyOS4zODc2IDg0LjA3MzQgMjkuMDQ5OCA4NC40OTMyIDI4LjQ0ODIgODYuMDA0NUMyOC4wMzQ3IDg3LjA0MzIgMjcuNDkxNSA4OC4xMTUzIDI3LjI0MTIgODguMzg2M0MyNi45OTA5IDg4LjY1NzMgMjYuNDc1IDg5LjU1MSAyNi4wOTQxIDkwLjM3MzJDMjQuOTQyMiA5Mi44NTkyIDIzLjUyODIgOTQuMDYxOCAxOS45MDExIDk1LjY0MjFDMTYuNjEwNyA5Ny4wNzU2IDE2LjU2NyA5Ny4xMDg2IDE1LjY2NzEgOTguOTgwMkMxNS4xMDI3IDEwMC4xNTQgMTQuNjc5MiAxMDEuNzU3IDE0LjU0OTkgMTAzLjIwNUMxNC4yNzMxIDEwNi4zMDIgMTMuNTk2MiAxMDcuMDMxIDkuNTIxMTggMTA4LjYxNkM1LjkxNDk3IDExMC4wMTkgMy41NzcwMiAxMTEuNzQ1IDMuNDE1ODMgMTEzLjEyM0MzLjI0MDQxIDExNC42MjMgMi41OTgxMyAxMTUuMDg0IDAuOTI5MjYgMTE0LjkwN0MwLjUzOTUyOSAxMTQuODY2IDAuMjQ0MzMyIDExNC44NTEgMCAxMTQuODgxVjEyMUMxLjAyNjU4IDExOS42NTggMi43MTkyMiAxMTguMzYxIDYuMjI0OTcgMTE2LjIwNkMxMC4zNjA0IDExMy42NjUgMTIuNzA2NSAxMTEuNTc1IDEzLjU3NzkgMTA5LjY1NEMxMy45MjYgMTA4Ljg4NyAxNC42NTQ3IDEwNy4zOTMgMTUuMTk3MSAxMDYuMzM0QzE1LjczOTUgMTA1LjI3NSAxNi4yODQ1IDEwMy45MDEgMTYuNDA4NCAxMDMuMjgxQzE2LjczMjQgMTAxLjY2IDE4LjMwMzIgMTAwLjM0OSAyMC42NjU5IDk5LjcyNkMyNS4xNzcgOTguNTM3MiAyNS43NDYxIDk4LjAxNjMgMjYuMzUyNSA5NC41MzU0QzI2Ljg1NjEgOTEuNjQ1MiAyOC42MTIgODguMDY2MSAzMC41MjY3IDg2LjAyNDZDMzEuMzA5NSA4NS4xOSAzMS45NDk1IDg0LjE4IDMxLjk0OTUgODMuNzc5MUMzMS45NDk1IDgzLjM3NjYgMzEuODg0OSA4My4yMTQ2IDMxLjcxNjYgODMuMjU5OFoiIGZpbGw9IiMzNTkxMzYiLz4KPHBhdGggZD0iTTguMDgxNzcgNzQuNDA4M0M2LjAzNDE5IDczLjg3MDUgNS44OTQ0MyA3My43MzAyIDYuMDQyNzEgNzIuMzYxNUM2LjEzMDI1IDcxLjU1MzMgNi4yNDAxNyA3MC44NTc1IDYuMjg2OTYgNzAuODE1NEM2LjMzMzc2IDcwLjc3MzMgNy43MDQ0MyA3MS4wNjg3IDkuMzMyOTIgNzEuNDcxOUMxMS45NzY4IDcyLjEyNjQgMTIuNDg2NiA3Mi4xMjM4IDE0LjA5NDcgNzEuNDQ3NEMxNi40MDQxIDcwLjQ3NiAxNy4yMzgzIDY4Ljc3NTMgMTYuNDAyMiA2Ni43NDMxQzE1Ljk0MjQgNjUuNjI1NyAxNC45MjA0IDY0LjgyODEgMTIuMjM4NCA2My40OTRDNy42NDE3IDYxLjIwNzUgNi41MzA2MiA1OS45OTkgNi4yNzQ2NSA1Ny4wMDc2QzUuODkzMzcgNTIuNTUxOSA4Ljg0Njg5IDQ5Ljk4MzYgMTQuMzUyMiA0OS45ODM2QzE1Ljk3MzMgNDkuOTgzNiAxNy44NDg3IDUwLjE5MzcgMTguNTE5NiA1MC40NTA1QzE5LjYzNjggNTAuODc4MSAxOS42OTM3IDUxLjAzOTkgMTkuMTk0NSA1Mi4zNzEyQzE4LjcxODIgNTMuNjQxNiAxOC41MDI3IDUzLjc2ODggMTcuNDg2MSA1My4zNzk2QzE1LjUxOTkgNTIuNjI3IDEyLjE5NDggNTIuODU1NCAxMS4wMDU4IDUzLjgyNDdDOS43OTA1OCA1NC44MTUzIDkuNTU3MzYgNTYuOTI1MiAxMC41MzI0IDU4LjEwNzlDMTAuODcyNyA1OC41MjA3IDEyLjkwODcgNTkuNzc5IDE1LjA1NjkgNjAuOTA0MUMxOC40NjI4IDYyLjY4ODEgMTkuMDgyIDYzLjIyMTQgMTkuODk1MSA2NS4wNzE5QzIxLjExNTIgNjcuODQ4NyAyMC42Nzg4IDcwLjM0MzcgMTguNjA3OCA3Mi40MzIzQzE2LjE2MDcgNzQuOTAwMiAxMi41NDEzIDc1LjU3OTYgOC4wODE3NyA3NC40MDgzWk0yOC45MzI4IDc0LjM4MjNDMjUuMjM2NSA3Mi45OTU4IDIzLjcxMzggNzAuNTQwNSAyMy43MTM4IDY1Ljk2NjdDMjMuNzEzOCA2Mi41MDM0IDI0LjU1ODggNjAuNDY5NyAyNi44MzMzIDU4LjQ1OTNDMjguMzk2NCA1Ny4wNzc2IDI4Ljk4NjcgNTYuODY4NiAzMS4zMjY4IDU2Ljg2ODZDMzIuODE2OCA1Ni44Njg2IDM0LjY4MjEgNTcuMjEyOSAzNS41MDA1IDU3LjYzODlDMzcuMzE2OSA1OC41ODQ2IDM4LjgzMTkgNjEuNzQyNiAzOC44NDY2IDY0LjYxNDNMMzguODU3MyA2Ni43MDQ0SDMyLjk0MjNIMjcuMDI3M0wyNy4zNDIgNjguMDU2OUMyNy41MTUgNjguODAwNyAyOC4zMzU0IDcwLjAzOTEgMjkuMTY1IDcwLjgwOUMzMC41NzczIDcyLjExOTYgMzAuODk0OSA3Mi4xOTc5IDM0LjE1NDcgNzIuMDM4NUMzNy4zOTM4IDcxLjg4MDEgMzcuNjQ2MyA3MS45NDA5IDM3Ljc4NCA3Mi45MTIyQzM3Ljk2MjYgNzQuMTcyNSAzNy4xODgyIDc0LjUyMzYgMzMuNTI0MiA3NC44NDM0QzMxLjgwODUgNzQuOTkzMiAzMC4xMDU2IDc0LjgyMjIgMjguOTMyOCA3NC4zODIzWk0zNS4wODgxIDYyLjUyNDJDMzQuOTU1OCA2MC42NzMgMzMuMzY0NCA1OS4zMjc2IDMxLjMwNzIgNTkuMzI3NkMyOS43MDg2IDU5LjMyNzYgMjguNzI4MSA2MC4wOTA5IDI3LjY2MiA2Mi4xNjU1QzI2Ljc0NTcgNjMuOTQ4NiAyNy40MTIgNjQuMjk2NSAzMS40NDg4IDY0LjE0MjVMMzUuMTkzNiA2My45OTk2TDM1LjA4ODEgNjIuNTI0MlpNNDQuMzkxNCA3NC4zNzA4QzQyLjU3MTEgNzMuNTQ2OCA0MS43MDU5IDcyLjEyODYgNDEuNjQyMSA2OS44NjQ3QzQxLjU0MTYgNjYuMjk0NCA0NC4xMTEgNjQuMTQ2OCA0OS4yNTc2IDYzLjQ5OTRDNTEuODQxNCA2My4xNzQ0IDUyLjA0OTUgNjMuMDU3NyA1MS43NTkzIDYyLjA5NjlDNTAuOTY1IDU5LjQ2NjYgNDguMjQyMyA1OC41NjQ0IDQ0LjkyNzYgNTkuODMzMUM0My40MzAzIDYwLjQwNjMgNDMuNDg2OCA2MC40MzEzIDQzLjA1MTQgNTkuMDAzMkM0Mi43ODAxIDU4LjExMzUgNDIuOTk4MiA1Ny44NTExIDQ0LjM5NSA1Ny4zODdDNDYuOTM2NCA1Ni41NDI3IDUxLjM3NDUgNTYuNjM0MiA1Mi43Nzc5IDU3LjU2QzU0LjkzOTIgNTguOTg1NyA1NS4zNzUxIDYwLjYwMTkgNTUuNTQ4NyA2Ny44MzI4TDU1LjcxMDYgNzQuNTc0NUw1NC4xMjMgNzQuNTc0QzUzLjAzNzcgNzQuNTczMyA1Mi41MzUzIDc0LjMzOTYgNTIuNTM1MyA3My44MzU0QzUyLjUzNTMgNzIuOTA2NiA1Mi4wNTI4IDcyLjkwMjQgNTAuNzUyIDczLjgxOTZDNDkuMjAxMyA3NC45MTMgNDYuMTY5NyA3NS4xNzU3IDQ0LjM5MTQgNzQuMzcwOFpNNTAuOTQ3NyA3MC44MTc1QzUxLjcwOTkgNjkuOTczNSA1Mi4wNDY4IDY4Ljk4NiA1Mi4wNDY4IDY3LjU5NjZWNjUuNTkyOUw0OS44MTgyIDY1Ljg0NThDNDUuNzE5IDY2LjMxMSA0My44NzQ5IDY5LjUxMTggNDYuNTA1OCA3MS41OTUzQzQ4LjAzNzEgNzIuODA3OSA0OS4zNTk0IDcyLjU3NjMgNTAuOTQ3NyA3MC44MTc1Wk05Mi4wMDIgNzQuNTc4OEM4OC40MDI0IDczLjQ4NjQgODYuMjQxOSA3MC4yNDY2IDg2LjI0MTkgNjUuOTQxQzg2LjI0MTkgNjAuNDA0MiA4OS41MjExIDU2LjgzMjUgOTQuNjQxIDU2Ljc5MjVDOTguNjk4OSA1Ni43NjA5IDEwMS4yNjMgNTkuNDY3OSAxMDEuNTAzIDY0LjAzNjZMMTAxLjYzIDY2LjQ1ODVMOTYuMjU2MiA2Ni41ODg1QzkzLjMwMDcgNjYuNjYgOTAuNjE1OCA2Ni44MTYgOTAuMjg5NyA2Ni45MzUxQzg5LjM4MTggNjcuMjY2OCA5MC40Nzg4IDY5Ljk0NyA5Mi4wMzA2IDcxLjE4ODZDOTMuMTU5OSA3Mi4wOTIxIDkzLjc3MSA3Mi4xOTEyIDk2LjgxNzggNzEuOTY1QzEwMC4xOTMgNzEuNzE0NCAxMDAuMzE4IDcxLjc0NDkgMTAwLjUzMSA3Mi44Njg0QzEwMC43MyA3My45MTMzIDEwMC41MDkgNzQuMDgzNSA5OC4zNTc5IDc0LjU0OEM5NS42MTAzIDc1LjE0MTIgOTMuODgyOSA3NS4xNDk2IDkyLjAwMiA3NC41Nzg4Wk05Ny45NjU5IDYzLjA4MjNDOTcuOTY1OSA2MS4xMjMxIDk2LjE4NTggNTkuMzI3NiA5NC4yNDM0IDU5LjMyNzZDOTIuMjQ5NCA1OS4zMjc2IDkwLjE0OTkgNjEuMjU2MiA5MC4xNDk5IDYzLjA4NzhDOTAuMTQ5OSA2NC4yMDcgOTAuMjc5NyA2NC4yNDU1IDk0LjA1NzkgNjQuMjQ1NUM5Ny44NDE0IDY0LjI0NTUgOTcuOTY1OSA2NC4yMDg0IDk3Ljk2NTkgNjMuMDgyM1pNMTEwLjA0MiA3NC40N0MxMDYuMTQ5IDczLjQzIDEwMy45MyA2OS40MDI5IDEwNC41NCA2NC40ODYyQzEwNS4xNjYgNTkuNDQ0MiAxMDcuOTUxIDU2Ljg3MzYgMTEyLjc5MiA1Ni44NzA1QzExNS4zNzQgNTYuODY5IDExNS44MDUgNTcuMDI0OSAxMTcuMjYgNTguNDlDMTE4Ljg3OCA2MC4xMTg1IDExOS45NDEgNjIuODM0MyAxMTkuOTQ2IDY1LjM1MkwxMTkuOTQ5IDY2LjcwNDRIMTE0LjA4N0MxMDguNDg4IDY2LjcwNDQgMTA4LjIyNSA2Ni43NTAxIDEwOC4yMjUgNjcuNzIwM0MxMDguMjI1IDcwLjcxMjIgMTEwLjk1MiA3Mi4zNDQyIDExNS4zNTcgNzEuOTg4MkMxMTguMTM2IDcxLjc2MzcgMTE4LjQyMyA3MS44MzE1IDExOC42NjcgNzIuNzY3NEMxMTguOTk4IDc0LjA0NDMgMTE4Ljk2OCA3NC4wNzIgMTE2LjY5IDc0LjYwNDNDMTE0LjM2NyA3NS4xNDcgMTEyLjQzIDc1LjEwNzggMTEwLjA0MiA3NC40N1pNMTE2LjExOSA2Mi43NzAxQzExNS44NjQgNjAuODc4NyAxMTQuMTU5IDU5LjMyNzYgMTEyLjMzNSA1OS4zMjc2QzExMC41NjYgNTkuMzI3NiAxMDguMjI0IDYxLjYwMTQgMTA4LjIyNCA2My4zMTk1QzEwOC4yMjQgNjQuMjEyOCAxMDguNTI3IDY0LjI3NDYgMTEyLjI1NSA2NC4xNDI0QzExNi4yNDEgNjQuMDAxMSAxMTYuMjgzIDYzLjk4NjIgMTE2LjExOSA2Mi43NzAxWk0xMjYuODk0IDc0LjI0N0MxMjQuODI2IDczLjIyMDggMTIzLjUzMiA3MS4zNTg5IDEyMi44OTQgNjguNDg4OUMxMjEuOTc1IDY0LjM2MDUgMTIzLjA3NCA2MC40OTE0IDEyNS44MyA1OC4xNTY3QzEyNy44MjcgNTYuNDY1NyAxMzEuODM4IDU2LjMzMDUgMTMzLjgwNSA1Ny44ODc5TDEzNS4wOTIgNTguOTA3MlY1My45NTM2VjQ5SDEzNy4wNDZIMTM5VjYxLjc4NjVWNzQuNTczMUgxMzcuMjlDMTM1Ljc2MiA3NC41NzMxIDEzNS41OCA3NC40MzMxIDEzNS41OCA3My4yNTc5VjcxLjk0MjdMMTM0LjQ4MSA3Mi45NzkyQzEzMi4yMzMgNzUuMDk5OCAxMjkuNTI1IDc1LjU1MjMgMTI2Ljg5NCA3NC4yNDdaTTEzMy42NjQgNzAuNjc2NkMxMzQuOTc2IDY5LjM1NTcgMTM1LjA5MiA2OC45NTM3IDEzNS4wOTIgNjUuNzIwOUMxMzUuMDkyIDYyLjQ1NzIgMTM0Ljk4NSA2Mi4wOTQ1IDEzMy42MDcgNjAuNzA3NkMxMzIuMzY1IDU5LjQ1NzUgMTMxLjg2NSA1OS4yNjIyIDEzMC41NTQgNTkuNTE1NEMxMjYuMTQ0IDYwLjM2NzIgMTI0LjgzNSA2OC4xNDIzIDEyOC41OTggNzEuMTIyOUMxMzAuMzQ4IDcyLjUwODEgMTMxLjk4OCA3Mi4zNjM1IDEzMy42NjQgNzAuNjc2NlpNNjEuNDEwNyA2Ni4wNTg0QzYwLjAyMiA2MS4zNzU0IDU4Ljg4NTkgNTcuMzkxOSA1OC44ODU5IDU3LjIwNjJDNTguODg1OSA1Ny4wMjA2IDU5LjcyNzQgNTYuODY4NiA2MC43NTU5IDU2Ljg2ODZINjIuNjI1OUw2NC4yNTUxIDYzLjg3NjZDNjUuMTUxMiA2Ny43MzEgNjUuOTU2IDcwLjY2MzMgNjYuMDQzNyA3MC4zOTI5QzY2LjEzMTMgNzAuMTIyNCA2Ny4wNTU3IDY3LjAyNDEgNjguMDk3OCA2My41MDc4TDY5Ljk5MjYgNTcuMTE0NUg3MS41OTY5SDczLjIwMTJMNzQuODg2NSA2My4yNjE5Qzc1LjgxMzQgNjYuNjQzIDc2LjY4MzIgNjkuNzQxMiA3Ni44MTk0IDcwLjE0N0M3Ni45NTU3IDcwLjU1MjcgNzcuODQxOSA2Ny43MzEgNzguNzg5IDYzLjg3NjZMODAuNTEwOSA1Ni44Njg2SDgyLjQ1MTdDODMuOTUxNyA1Ni44Njg2IDg0LjMzMDMgNTcuMDMxNiA4NC4xMTkgNTcuNTg2QzgzLjk2ODYgNTcuOTgwNiA4Mi42OTU2IDYxLjk2NDEgODEuMjkwMiA2Ni40MzgyTDc4LjczNDkgNzQuNTczMUg3Ni45NjM1SDc1LjE5MjFMNzMuNTgxMiA2OC43OTQ1QzcyLjY5NTIgNjUuNjE2MyA3MS44NTg1IDYyLjY4NDEgNzEuNzIxNyA2Mi4yNzgzQzcxLjQ0NjUgNjEuNDYxNiA3MS41OTExIDYxLjA3MzMgNjkuMTExNCA2OS4yODYzTDY3LjUxNTIgNzQuNTczMUg2NS43MjU0SDYzLjkzNTZMNjEuNDEwNyA2Ni4wNTg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTE5Ljk3NDYgMTE5LjI3NEMxOS4wMTU4IDExOC4xMTkgMjAuMzM4MSAxMTYuMTY2IDIxLjU2MDQgMTE2LjkzMkMyMi43NjMyIDExNy42ODYgMjIuMzMwNSAxMTkuNzYxIDIwLjk3MDQgMTE5Ljc2MUMyMC42NDUgMTE5Ljc2MSAyMC4xOTY5IDExOS41NDIgMTkuOTc0NiAxMTkuMjc0WiIgZmlsbD0idXJsKCNwYWludDNfcmFkaWFsXzY3OV8xOTEwKSIvPgo8cGF0aCBkPSJNMjMuODM2IDExMi42M0MyMi4xMTUyIDExMS42ODkgMjEuODAyOSAxMDkuNzk3IDIzLjE0NjUgMTA4LjQ1M0MyNC4wODU3IDEwNy41MTQgMjUuMjEgMTA3LjM5NCAyNi4yNTA3IDEwOC4xMjNDMjcuODU3MSAxMDkuMjQ5IDI3LjQ2NTUgMTEyLjAxMyAyNS42MDc5IDExMi42NkMyNS42MDc5IDExMi42NiAyNC40NzQ1IDExMi45NzMgMjMuODM2IDExMi42M1oiIGZpbGw9InVybCgjcGFpbnQ0X3JhZGlhbF82NzlfMTkxMCkiLz4KPHBhdGggZD0iTTMzLjc4NTUgMTIwLjAzNkMzMy43ODU1IDEyMC4xODggMzMuNzU1NyAxMjAuMzM4IDMzLjY5NzYgMTIwLjQ3OUMzMy42Mzk1IDEyMC42MTkgMzMuNTU0NCAxMjAuNzQ2IDMzLjQ0NzEgMTIwLjg1M0MzMy4zMzk4IDEyMC45NjEgMzMuMjEyNCAxMjEuMDQ2IDMzLjA3MjMgMTIxLjEwNEMzMi45MzIxIDEyMS4xNjIgMzIuNzgxOCAxMjEuMTkyIDMyLjYzMDEgMTIxLjE5MkMzMi40NzgzIDEyMS4xOTIgMzIuMzI4MSAxMjEuMTYyIDMyLjE4NzkgMTIxLjEwNEMzMi4wNDc3IDEyMS4wNDYgMzEuOTIwMyAxMjAuOTYxIDMxLjgxMyAxMjAuODUzQzMxLjcwNTcgMTIwLjc0NiAzMS42MjA2IDEyMC42MTkgMzEuNTYyNiAxMjAuNDc5QzMxLjUwNDUgMTIwLjMzOCAzMS40NzQ2IDEyMC4xODggMzEuNDc0NiAxMjAuMDM2QzMxLjQ3NDYgMTE5LjczIDMxLjU5NjMgMTE5LjQzNiAzMS44MTMgMTE5LjIxOUMzMi4wMjk3IDExOS4wMDMgMzIuMzIzNiAxMTguODgxIDMyLjYzMDEgMTE4Ljg4MUMzMi45MzY1IDExOC44ODEgMzMuMjMwNCAxMTkuMDAzIDMzLjQ0NzEgMTE5LjIxOUMzMy42NjM4IDExOS40MzYgMzMuNzg1NSAxMTkuNzMgMzMuNzg1NSAxMjAuMDM2WiIgZmlsbD0iYmxhY2siLz4KPHBhdGggZD0iTTEyMC43MjEgMTE4LjI3NkMxMjAuNzIxIDExOC42MjMgMTIwLjU2NSAxMTguOTU2IDEyMC4yODcgMTE5LjIwMUMxMjAuMDA5IDExOS40NDYgMTE5LjYzMiAxMTkuNTg0IDExOS4yMzkgMTE5LjU4NEMxMTguODQ2IDExOS41ODQgMTE4LjQ2OSAxMTkuNDQ2IDExOC4xOTEgMTE5LjIwMUMxMTcuOTEzIDExOC45NTYgMTE3Ljc1NyAxMTguNjIzIDExNy43NTcgMTE4LjI3NkMxMTcuNzU3IDExOC4xMDUgMTE3Ljc5NSAxMTcuOTM1IDExNy44NyAxMTcuNzc2QzExNy45NDQgMTE3LjYxNyAxMTguMDUzIDExNy40NzMgMTE4LjE5MSAxMTcuMzUyQzExOC4zMjkgMTE3LjIzIDExOC40OTIgMTE3LjEzNCAxMTguNjcyIDExNy4wNjhDMTE4Ljg1MSAxMTcuMDAzIDExOS4wNDQgMTE2Ljk2OSAxMTkuMjM5IDExNi45NjlDMTE5LjQzMyAxMTYuOTY5IDExOS42MjYgMTE3LjAwMyAxMTkuODA2IDExNy4wNjhDMTE5Ljk4NiAxMTcuMTM0IDEyMC4xNDkgMTE3LjIzIDEyMC4yODcgMTE3LjM1MkMxMjAuNDI0IDExNy40NzMgMTIwLjUzMyAxMTcuNjE3IDEyMC42MDggMTE3Ljc3NkMxMjAuNjgyIDExNy45MzUgMTIwLjcyMSAxMTguMTA1IDEyMC43MjEgMTE4LjI3NloiIGZpbGw9IiM1OTY4NkYiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82NzlfMTkxMCIgeDE9IjExIiB5MT0iLTQuNzc3NjhlLTA3IiB4Mj0iMTY0LjUiIHkyPSIxNTAuNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjNjRCN0ZGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwNURBRCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8cmFkaWFsR3JhZGllbnQgaWQ9InBhaW50MV9yYWRpYWxfNjc5XzE5MTAiIGN4PSIwIiBjeT0iMCIgcj0iMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI0LjkwNCAxMjguNjM2KSByb3RhdGUoLTAuMDQ4NTUyKSBzY2FsZSgzNi4yODAyIDMxLjQwMzIpIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzExNzdDRSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM3RUM3RkYiIHN0b3Atb3BhY2l0eT0iMCIvPgo8L3JhZGlhbEdyYWRpZW50Pgo8cmFkaWFsR3JhZGllbnQgaWQ9InBhaW50Ml9yYWRpYWxfNjc5XzE5MTAiIGN4PSIwIiBjeT0iMCIgcj0iMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzkuMzUxMiAxMjEuNDYpIHJvdGF0ZSgtMC4wNTczKSBzY2FsZSg1MC4yMjc2IDUxLjMwOTEpIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzExNzdDRSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM3RUM3RkYiIHN0b3Atb3BhY2l0eT0iMCIvPgo8L3JhZGlhbEdyYWRpZW50Pgo8cmFkaWFsR3JhZGllbnQgaWQ9InBhaW50M19yYWRpYWxfNjc5XzE5MTAiIGN4PSIwIiBjeT0iMCIgcj0iMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjAuOTU0OCAxMTguMjYpIHNjYWxlKDEuMzA5MjkgMS41MDEyNykiPgo8c3RvcCBzdG9wLWNvbG9yPSIjODhBM0QwIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzVEODNCRiIvPgo8L3JhZGlhbEdyYWRpZW50Pgo8cmFkaWFsR3JhZGllbnQgaWQ9InBhaW50NF9yYWRpYWxfNjc5XzE5MTAiIGN4PSIwIiBjeT0iMCIgcj0iMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjQuNzg0MyAxMTAuMjIxKSBzY2FsZSgyLjQ3Mjc3IDIuNTY5NTQpIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzg4QTNEMCIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM1RDgzQkYiLz4KPC9yYWRpYWxHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "host"], ["spec", "topology"], ["spec", "replicationFactor"], ["spec", "db"], ["spec", "db", "replicas"], ["spec", "db", "size"], ["spec", "db", "storageClass"], ["spec", "db", "resources"], ["spec", "db", "resourcesPreset"], ["spec", "master"], ["spec", "master", "replicas"], ["spec", "master", "resources"], ["spec", "master", "resourcesPreset"], ["spec", "filer"], ["spec", "filer", "replicas"], ["spec", "filer", "resources"], ["spec", "filer", "resourcesPreset"], ["spec", "filer", "grpcHost"], ["spec", "filer", "grpcPort"], ["spec", "filer", "whitelist"], ["spec", "volume"], ["spec", "volume", "replicas"], ["spec", "volume", "size"], ["spec", "volume", "storageClass"], ["spec", "volume", "resources"], ["spec", "volume", "resourcesPreset"], ["spec", "volume", "zones"], ["spec", "s3"], ["spec", "s3", "replicas"], ["spec", "s3", "resources"], ["spec", "s3", "resourcesPreset"]] + secrets: + exclude: [] + include: [] + services: + exclude: [] + include: + - resourceNames: + - seaweedfs-{{ .name }}-s3 + ingresses: + exclude: [] + include: + - resourceNames: + - ingress-seaweedfs-{{ .name }}-s3 diff --git a/packages/system/seaweedfs-rd/templates/cozyrd.yaml b/packages/system/seaweedfs-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/seaweedfs-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/seaweedfs-rd/values.yaml b/packages/system/seaweedfs-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/seaweedfs-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/tcp-balancer-rd/Chart.yaml b/packages/system/tcp-balancer-rd/Chart.yaml new file mode 100644 index 00000000..9543455e --- /dev/null +++ b/packages/system/tcp-balancer-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: tcp-balancer-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/tcp-balancer-rd/Makefile b/packages/system/tcp-balancer-rd/Makefile new file mode 100644 index 00000000..39a0495f --- /dev/null +++ b/packages/system/tcp-balancer-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=tcp-balancer-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml b/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml new file mode 100644 index 00000000..057bc922 --- /dev/null +++ b/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml @@ -0,0 +1,33 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: tcp-balancer +spec: + application: + kind: TCPBalancer + plural: tcpbalancers + singular: tcpbalancer + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"httpAndHttps":{"description":"HTTP and HTTPS configuration.","type":"object","default":{},"required":["mode","targetPorts"],"properties":{"endpoints":{"description":"Endpoint addresses list.","type":"array","default":[],"items":{"type":"string"}},"mode":{"description":"Mode for balancer.","type":"string","default":"tcp","enum":["tcp","tcp-with-proxy"]},"targetPorts":{"description":"Target ports configuration.","type":"object","default":{},"required":["http","https"],"properties":{"http":{"description":"HTTP port number.","type":"integer","default":80},"https":{"description":"HTTPS port number.","type":"integer","default":443}}}}},"replicas":{"description":"Number of HAProxy replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each TCP Balancer replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"whitelist":{"description":"List of allowed client networks.","type":"array","default":[],"items":{"type":"string"}},"whitelistHTTP":{"description":"Secure HTTP by whitelisting client networks (default: false).","type":"boolean","default":false}}} + release: + prefix: tcp-balancer- + labels: + cozystack.io/ui: "true" + chart: + name: tcp-balancer + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: NaaS + singular: TCP Balancer + plural: TCP Balancer + description: Layer4 load balancer service + tags: + - network + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMjk4NSkiLz4KPHBhdGggZD0iTTcyLjE2NDUgNTkuNDI0M0w2Mi41IDQ4LjQ5OCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjI2IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTQxLjY4NTUgNTEuMDIxNUw0OS4yNDcgNjIuMDY5MyIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEzIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTcyLjE2NDYgNTkuNDQxNEw4MS43MjYzIDQ4LjM5MzZNODMuNzE3MSA3MS42Mjk0TDk1LjA2NCA2Mi4wNjc4TTgzLjcxNzEgNzEuNjEwOEw5NS4wNjQgODEuNTkzTTgxLjgyOTEgOTQuMjAxN0w3Mi4xNjQ2IDgzLjQ4MTFNNjIuNTAwMSA5NC4yMDE3TDcyLjE2NDYgODMuNDkwNE00OS4yNTU5IDgxLjE3MjRMNjAuMTgyMSA3MS42MTA4TTYwLjE5MTUgNzEuNjEwOEw0OS4yNjUyIDYyLjA0OTFMNzIuMTY0NiA1OS40MjI3TDk1LjA2NCA2Mi4wNDkxIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMjYiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNODEuNzE2OCA0OC4zOTM2TDgzLjcxNyA3MS42MTA4TDgxLjgyOSA5NC4xOTI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMjYiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNOTUuMDY0IDgxLjU5MjVMNzIuMTY0NiA4My40ODA1TTQ5LjI1NTkgODEuMTcxOUw3Mi4xNTUzIDgzLjQ5OTIiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4yNiIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik02Mi41MDEyIDk0LjIwMTRMNjAuMTczOCA3MS42MTk4TTYyLjUwMTIgNDguNDk2MUw2MC4xNzM4IDcxLjYxMDUiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4yNiIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik0zOC43NSA2NC45OTQ4TDQ5LjI1NTcgNjIuMDUwNk01MS40NjE1IDQwLjYxODdMNjIuNTA5MyA0OC40OTc5TTYyLjQ5MDYgNDguNDk3OUw2NS4wMTQyIDM1LjY4MzZMODEuNzE2OCA0OC4zOTUxIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTMiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNNzkuMjAzMSAzNS42ODM2TDgxLjcyNjcgNDguMzk1MU05Mi43NTU4IDQwLjYxODdMODEuNzA4IDQ4LjM5NTFNMTAyLjYyNiA1MS4wMjE1TDgxLjcxNzQgNDguMzk1MU0xMDIuNjI2IDUxLjAwMjhMOTUuMDY0NSA2Mi4wNTA2TDEwNS43NzYgNjQuOTk0OCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEzIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTk1LjA2MzkgNjIuMDUwNkwxMDUuNTcgNzguMDE0OE03OS4yMDI1IDM1LjY4MzZMNjIuNSA0OC40OTc5IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTMiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNOTUuMDYzNyA2Mi4wNTExTDkyLjczNjMgNDAuNjE5MU05NS4wNjM3IDgxLjU5NTFMMTA1LjU2OSA3OC4wMTUzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTMiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNOTUuMDY1IDgxLjU5NDlMMTA1Ljc3NiA2NC45OTUxTTk1LjA2NSA4MS41OTQ5TDEwMi42MjYgOTEuNzgyOE05NS4wNjUgODEuNTk0OUw5Mi43Mzc3IDEwMi4xODZNODEuODMwMSA5NC4yMDM1TDkyLjc1NjQgMTAyLjE4NiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEzIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTgxLjgyOSA5NC4yMDNMMTAyLjYyNSA5MS43ODIyTTgxLjgyOSA5NC4yMDNMNzkuMzI0MSAxMDcuMTExTTgxLjgyOSA5NC4yMDNMNjUuMDE0MyAxMDcuMTAxTTYyLjUgOTQuMjAzTDY1LjAyMzYgMTA3LjEzOSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEzIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTYyLjUwMTIgOTQuMjAzMUw3OS4zMjUzIDEwNy4xMTFNNTEuNDYyOCAxMDIuMTg1TDYyLjUxMDUgOTQuMjAzMUw0MS42NzY4IDkxLjg4NTFMNDkuMjU2OSA4MS4xNzM4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTMiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNNTEuNDYxOCAxMDIuMTg1TDQ5LjI1NiA4MS4xNzMxTDM4LjY0NzUgNzcuOTExMUw0OS4yNTYgNjIuMDQ5OCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEzIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTQ5LjI1NTcgODEuMTc0NUwzOC43NSA2NC45OTUzTTUxLjQ2MTUgNDAuNjE5MUw0OS4yNTU3IDYyLjA1MTEiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4xMyIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik00MS42ODU1IDUxLjAyMTdMNjIuNDgxOSA0OC40OTgiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4xMyIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik00My4zNzYgMzYuODMzOEw1MS40NjA5IDQwLjYxOTJMNTAuODM0NiAzMS43OTU5TDY1LjAxMzYgMzUuNjg0MSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEwMSIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik01OC45Mjg5IDI4LjIyNDRMNjUuMDEzNiAzNS42ODMxTDY3LjUzNzMgMjYuNzM4M0w3OS4yMDE5IDM1LjY4MzFNNTEuNDYwOSA0MC42MTgxTDU4LjkxOTYgMjguMjI0NCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEwMSIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik03Ni41NjY3IDI2LjQ0MDRMNzkuMjAyNSAzNS42ODQzTDg1LjI5NjYgMjguMjI1Nk02NS4wMDQ5IDM1LjY4NDNMNzYuNTU3NCAyNi40NDA0TTkyLjc1NTIgNDAuNjE5NEw4NS4yNzc5IDI4LjIyNTYiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4xMDEiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNOTIuNzU0OSA0MC42MTkyTDkzLjQ5MzIgMzEuNzk1OU05Mi43NTQ5IDQwLjYxOTJMMTAxLjAzNiAzNi40MTMyTTkyLjc1NDkgNDAuNjE5MkwxMDcuMTQ5IDQyLjgyNU03OS4yMDIxIDM1LjY4NDFMOTMuNDgzOSAzMS43OTU5IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTAxIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTEwMi42MjUgNTEuMDIxNEwxMDcuMTM5IDQyLjgyNDRNMTAyLjYyNSA1MS4wMjE0TDEwMS4wNDUgMzYuNDIxOU0xMDIuNjI1IDUxLjAyMTRMMTEyLjA4MyA1MC4yODNNMTAyLjYyNSA1MS4wMjE0TDExNS41NiA1OC4zNzczIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTAxIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTEwNS43NzQgNjQuOTk0OUwxMTIuMDgzIDUwLjI4MzJNMTA1Ljc3NCA2NC45OTQ5TDExNS41NDEgNTguMzc3NE0xMDUuNzc0IDY0Ljk5NDlMMTE2LjcgNjcuMjAwN0wxMDUuNTY4IDc4LjAxNDkiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4xMDEiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNMTE3LjAxOSA3NS45MTIxTDEwNS41NjkgNzguMDE1MUwxMTUuMzM3IDg0LjUyOTdMMTAyLjYyNSA5MS43ODI3TTEwNS43NzUgNjQuOTk1MUwxMTcuMDE5IDc1LjkyMTQiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4xMDEiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNMTAyLjYyNSA5MS43ODMzTDExMS45NzEgOTIuNzI3M00xMDUuNTc4IDc4LjAxNTZMMTExLjk3MSA5Mi43MThNMTA3LjA1NSAxMDAuMDY0TDEwMi42NDMgOTEuNzgzM0wxMDAuOTYxIDEwNi4yOCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEwMSIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik0xMDAuOTQzIDEwNi4yNzlMOTIuNzQ1OCAxMDIuMTg1TTkyLjc1NTEgMTAyLjE4NUw5My4zODEzIDExMS4zNDVNOTIuNzU1MSAxMDIuMTg1TDEwNy4wMzcgMTAwLjA4Mk05Mi43NTUxIDEwMi4xODVMODUuMjg3MSAxMTQuNzk0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTAxIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTc5LjMyMzIgMTA3LjEwM0w4NS4yNzcgMTE0Ljc2N003OS4zMjMyIDEwNy4xMDNMOTMuMzk5MyAxMTEuMzA5TTc5LjMyMzIgMTA3LjEwM0w3Ni41NjU5IDExNi41NjFNNzkuMzIzMiAxMDcuMTAzTDY3LjU1NTcgMTE2LjU2MSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEwMSIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik02NS4wMTM2IDEwNy4xMDJMNjcuNTM3MyAxMTYuNTYxTTY1LjAxMzYgMTA3LjEwMkw3Ni41NTY4IDExNi41NjFNNjUuMDEzNiAxMDcuMTAyTDU5LjAyMjQgMTE0Ljc2Nk01MS40NjA5IDEwMi4xODZMNTkuMDIyNCAxMTQuNzk0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTAxIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTY1LjAxMzkgMTA3LjEwMUw1MC43MzIyIDExMS41MTNNNTAuNzIyOCAxMTEuNTMyTDUxLjQ2MTIgMTAyLjE4NUw0My4yNjQyIDEwNi42OTlMNDEuNjg0NiA5MS44ODQ4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTAxIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTUxLjQ2MSAxMDIuMTg2TDM3LjA2NzEgMTAwLjE5NU00MS42ODQ0IDkxLjg4NkwzNy4wNTc4IDEwMC4xNjdNNDEuNjg0NCA5MS44ODZMMzIuMjI1NSA5Mi44MzAxTTQxLjY4NDQgOTEuODg2TDI4LjY3MzggODQuNjQyM0wzOC42NDY3IDc3LjkxMjdMMjcuMjk5OCA3NS45MjE5IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTAxIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTM4LjY0NjcgNzcuOTEyM0wzMi4yNTM2IDkyLjgyOTZNMzguNzQ5NSA2NC45OTUxTDI3LjI5OTggNzUuOTIxNCIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEwMSIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik0zOC42NDY3IDc3LjkxMjJMMjcuMjk5OCA2Ny4yMDA5TTM4Ljc1ODkgNjQuOTk1MUwyOC45ODIyIDU4LjQ4MDUiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4xMDEiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNMjcuMjk5OCA2Ny4yMDA3TDM4Ljc0OTUgNjQuOTk0OUwzMi4yNDQyIDUwLjI4MzIiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMC4xMDEiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIvPgo8cGF0aCBkPSJNNDEuNjgzNiA1MS4wMjE3TDMyLjIyNDcgNTAuMjgzM000MS42ODM2IDUxLjAyMTdMMjguOTgxNCA1OC40ODA0TTQxLjY4MzYgNTEuMDIxN0wzNy4xNzg1IDQzLjI0NTNNNDEuNjgzNiA1MS4wMjE3TDQzLjM2NiAzNi44NDI4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuMTAxIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiLz4KPHBhdGggZD0iTTUxLjQ2MSA0MC42MTkxTDM3LjE2OTkgNDMuMjQ1NiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjEwMSIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIi8+CjxwYXRoIGQ9Ik01NC41NjQ1IDc3LjA3MDJMNTQuNjM5MiA2Ni4wMjI0TDY1LjY4NyA2Ni4wOTcxTDY1LjYxMjMgNzcuMTQ0OUw1NC41NjQ1IDc3LjA3MDJaTTY2LjUxODkgNjQuOTEwMUw2Ni41OTM3IDUzLjg2MjNMNzcuNjQxNSA1My45MzcxTDc3LjU2NjcgNjQuOTg0OUw2Ni41MTg5IDY0LjkxMDFaTTY2LjUxODkgODkuMTI3NEw2Ni41OTM3IDc4LjA3OTZMNzcuNjQxNSA3OC4xNTQ0TDc3LjU2NjcgODkuMjAyMkw2Ni41MTg5IDg5LjEyNzRaTTc4LjU3NjEgNzYuOTY3M0w3OC42NTA5IDY1LjkxOTVMODkuNjk4NyA2NS45OTQzTDg5LjYyMzkgNzcuMDQyMUw3OC41NzYxIDc2Ljk2NzNaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNzcuOTQwNiA1Mi4yMzY2TDc3Ljk5NjYgNDQuNTcyM0w4NS42OTgzIDQ0LjYyODNMODUuNjQyMiA1Mi4yOTI2TDc3Ljk0MDYgNTIuMjM2NlpNNTguNjg2NCA1Mi4yMzY2TDU4Ljc0MjQgNDQuNTcyM0w2Ni40NDQxIDQ0LjYyODNMNjYuMzg4IDUyLjI5MjZMNTguNjg2NCA1Mi4yMzY2Wk00NS4zNTggNjUuODkyMUw0NS40MTQxIDU4LjIyNzhMNTMuMTE1NyA1OC4yODM5TDUzLjA1OTcgNjUuOTQ4Mkw0NS4zNTggNjUuODkyMVpNNDUuMzQ4NiA4NS4xNjVMNDUuNDMyOCA3Ny41MDA3TDUzLjEzNDQgNzcuNTg0OEw1My4wNTAzIDg1LjI0OTFMNDUuMzQ4NiA4NS4xNjVaTTkxLjI2OSA4NS4wMTU0TDkxLjMyNSA3Ny4zNTExTDk5LjAyNjcgNzcuNDA3Mkw5OC45NzA2IDg1LjA3MTVMOTEuMjY5IDg1LjAxNTRaTTkxLjQxODUgNjUuOTQ4Mkw5MS41MDI2IDU4LjI4MzlMOTkuMjA0MyA1OC4zNjhMOTkuMTIwMiA2Ni4wMzIzTDkxLjQxODUgNjUuOTQ4MloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0zOS4xMTQzIDUzLjUyNjNMMzkuMTUxNyA0OC40NzkxTDQ0LjE4MDIgNDguNTE2NUw0NC4xNDI4IDUzLjU2MzdMMzkuMTE0MyA1My41MjYzWk00OC45NTYzIDQzLjI2MzdMNDguOTkzNyAzOC4yMTY0TDU0LjAyMjMgMzguMjUzOEw1My45ODQ5IDQzLjMwMUw0OC45NTYzIDQzLjI2MzdaTTYyLjQ5OTcgMzguMTg4NEw2Mi41MzcxIDMzLjE0MTJMNjcuNTY1NiAzMy4xNzg2TDY3LjUyODIgMzguMjI1OEw2Mi40OTk3IDM4LjE4ODRaTTM2LjE1MTQgNjcuNDkwM0wzNi4xODg4IDYyLjQ0MzFMNDEuMTg5MiA2Mi40ODA1TDQxLjE1MTkgNjcuNTI3N0wzNi4xNTE0IDY3LjQ5MDNaTTEwMC4wODMgNDguNTUzOUwxMDUuMTMgNDguNTE2NUwxMDUuMTY3IDUzLjU0NUwxMDAuMTIgNTMuNTgyNEwxMDAuMDgzIDQ4LjU1MzlaTTkwLjI0MDcgMzguMTEzNkw5NS4yODc5IDM4LjA3NjJMOTUuMzI1MyA0My4xMDQ4TDkwLjI3ODEgNDMuMTQyMUw5MC4yNDA3IDM4LjExMzZaTTc2LjY1MDYgMzMuMTY5Mkw4MS42OTc4IDMzLjEzMThMODEuNzM1MiAzOC4xNjA0TDc2LjY4OCAzOC4xOTc3TDc2LjY1MDYgMzMuMTY5MlpNMTAzLjAxOCA2Mi40OTkyTDEwOC4wNjUgNjIuNDYxOEwxMDguMTAyIDY3LjQ5MDNMMTAzLjA1NSA2Ny41Mjc3TDEwMy4wMTggNjIuNDk5MloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik01OC41ODMgOTguMjQwNUw1OC42MzkxIDkwLjU3NjJMNjYuMzQwOCA5MC42MzIzTDY2LjI4NDcgOTguMjk2NUw1OC41ODMgOTguMjQwNVpNNzcuODM3MiA5OC4yNDA1TDc3Ljg5MzMgOTAuNTc2Mkw4NS41OTUgOTAuNjMyM0w4NS41Mzg5IDk4LjI5NjVMNzcuODM3MiA5OC4yNDA1WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTEwMC4wNDUgOTQuMzYxOUwxMDAuMDgyIDg5LjMxNDdMMTA1LjExMSA4OS4zNTIxTDEwNS4wNzQgOTQuMzk5M0wxMDAuMDQ1IDk0LjM2MTlaTTkwLjMxNTIgMTA0LjcyN0w5MC4zNTI2IDk5LjY4MDJMOTUuMzgxMSA5OS43MTc2TDk1LjM0MzcgMTA0Ljc2NUw5MC4zMTUyIDEwNC43MjdaTTc2Ljc3MTggMTA5LjdMNzYuODA5MiAxMDQuNjUzTDgxLjgzNzcgMTA0LjY5TDgxLjgwMDQgMTA5LjczN0w3Ni43NzE4IDEwOS43Wk0xMDMuMDA4IDgwLjM5NzlMMTAzLjA0NSA3NS4zNTA3TDEwOC4wNzQgNzUuMzg4MUwxMDguMDM3IDgwLjQzNTNMMTAzLjAwOCA4MC4zOTc5Wk0zOS4xMjMzIDg5LjMxNDdMNDQuMTcwNiA4OS4yNzczTDQ0LjIwNzkgOTQuMzA1OEwzOS4xNjA3IDk0LjM0MzJMMzkuMTIzMyA4OS4zMTQ3Wk00OC45NjU0IDk5LjY0MjhMNTQuMDEyNiA5OS42MDU0TDU0LjA1IDEwNC42MzRMNDkuMDAyOCAxMDQuNjcxTDQ4Ljk2NTQgOTkuNjQyOFpNNjIuNDQzMyAxMDQuNTk3TDY3LjQ5MDYgMTA0LjU1OUw2Ny41Mjc5IDEwOS41ODhMNjIuNDgwNyAxMDkuNjI1TDYyLjQ0MzMgMTA0LjU5N1pNMzYuMTg4NSA3NS4zNjk0TDQxLjIzNTcgNzUuMzMyTDQxLjI3MzEgODAuMzYwNkwzNi4yMjU5IDgwLjM5NzlMMzYuMTg4NSA3NS4zNjk0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTExMC42MjUgNTEuNjM4NEwxMTAuNjQ0IDQ4LjkwOTJMMTEzLjQyOSA0OC45Mjc5TDExMy40MSA1MS42NTcxTDExMC42MjUgNTEuNjM4NFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMTQuMjI0IDU5Ljg5MTNMMTE0LjI0MiA1Ny4xNjIxTDExNy4wMjggNTcuMTgwOEwxMTcuMDA5IDU5LjkxTDExNC4yMjQgNTkuODkxM1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMDUuNzY3IDQ0LjMzNzZMMTA1Ljc4NSA0MS42MDg0TDEwOC41NzEgNDEuNjI3MUwxMDguNTUyIDQ0LjM1NjNMMTA1Ljc2NyA0NC4zMzc2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTQxLjk3MzYgMzguMDk0NUw0MS45OTIzIDM1LjM2NTJMNDQuNzc3NiAzNS4zODM5TDQ0Ljc1ODkgMzguMTEzMkw0MS45NzM2IDM4LjA5NDVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjYgNjguNTU1NEwyNi4wMTg3IDY1LjgyNjJMMjguODA0IDY1Ljg0NDlMMjguNzg1MyA2OC41NzQxTDI2IDY4LjU1NTRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTkuNjI0IDM4LjA5NDVMOTkuNjQyNyAzNS4zNjUyTDEwMi40MjggMzUuMzgzOUwxMDIuNDA5IDM4LjExMzJMOTkuNjI0IDM4LjA5NDVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjcuNTg5OCA1OS44ODE2TDI3LjYwODUgNTcuMTUyM0wzMC4zOTM5IDU3LjE3MUwzMC4zNzUyIDU5LjkwMDNMMjcuNTg5OCA1OS44ODE2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTU3LjYyOTkgMjkuNjM1NUw1Ny42NDg2IDI2LjkwNjJMNjAuNDMzOSAyNi45MjQ5TDYwLjQxNTIgMjkuNjU0Mkw1Ny42Mjk5IDI5LjYzNTVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzAuOTcyNyA1MS42Mzg0TDMwLjk5MTMgNDguOTA5MkwzMy43NzY3IDQ4LjkyNzlMMzMuNzU4IDUxLjY1NzFMMzAuOTcyNyA1MS42Mzg0WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTM1Ljk0NTMgNDQuMjM2MUwzNS45NjQgNDEuNTA2OEwzOC43NDkzIDQxLjUyNTVMMzguNzMwNiA0NC4yNTQ4TDM1Ljk0NTMgNDQuMjM2MVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik00OS40MjI5IDMwLjQyMDRMNTIuMTUyMSAzMC40MDE3TDUyLjE3MDggMzMuMTg3TDQ5LjQ0MTUgMzMuMjA1N0w0OS40MjI5IDMwLjQyMDRaTTY2LjMwMyAyNS4wNDZMNjkuMDMyMiAyNS4wMjczTDY5LjA1MDkgMjcuODEyN0w2Ni4zMjE3IDI3LjgzMTRMNjYuMzAzIDI1LjA0NloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik05Mi4yMjI3IDMzLjIyNDRMOTIuMjQxNCAzMC40OTUxTDk1LjAyNjcgMzAuNTEzOEw5NS4wMDggMzMuMjQzTDkyLjIyMjcgMzMuMjI0NFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMTUuNTk4IDY4LjQ1MjlMMTE1LjYxNiA2NS43MjM2TDExOC40MDIgNjUuNzQyM0wxMTguMzgzIDY4LjQ3MTZMMTE1LjU5OCA2OC40NTI5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTc1LjE5MTQgMjcuNzI5Mkw3NS4yMTAxIDI1TDc3Ljk5NTQgMjUuMDE4N0w3Ny45NzY3IDI3Ljc0NzlMNzUuMTkxNCAyNy43MjkyWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTgzLjg2NjIgMjkuNTIzMkw4My44ODQ5IDI2Ljc5MzlMODYuNjcwMiAyNi44MTI2TDg2LjY1MTUgMjkuNTQxOUw4My44NjYyIDI5LjUyMzJaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTkuNjI0IDEwNy43TDk5LjY0MjcgMTA0Ljk3MUwxMDIuNDI4IDEwNC45ODlMMTAyLjQwOSAxMDcuNzE5TDk5LjYyNCAxMDcuN1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMTUuNTk4IDc3LjIzOUwxMTUuNjE2IDc0LjUwOThMMTE4LjQwMiA3NC41Mjg1TDExOC4zODMgNzcuMjU3N0wxMTUuNTk4IDc3LjIzOVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik00MS45NzM2IDEwNy43TDQxLjk5MjMgMTA0Ljk3MUw0NC43Nzc2IDEwNC45ODlMNDQuNzU4OSAxMDcuNzE5TDQxLjk3MzYgMTA3LjdaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTE0LjIyNCA4Ni4wMzI5TDExNC4yNDIgODMuMzAzN0wxMTcuMDI4IDgzLjMyMjRMMTE3LjAwOSA4Ni4wNTE2TDExNC4yMjQgODYuMDMyOVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik04My44NjYyIDExNi4yN0w4My44ODQ5IDExMy41NDFMODYuNjcwMiAxMTMuNTZMODYuNjUxNSAxMTYuMjg5TDgzLjg2NjIgMTE2LjI3WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTExMC42MjUgOTQuMjY4M0wxMTAuNjQ0IDkxLjUzOTFMMTEzLjQyOSA5MS41NTc4TDExMy40MSA5NC4yODdMMTEwLjYyNSA5NC4yNjgzWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTEwNS43NjcgMTAxLjY3MkwxMDUuNzg1IDk4Ljk0MjRMMTA4LjU3MSA5OC45NjExTDEwOC41NTIgMTAxLjY5TDEwNS43NjcgMTAxLjY3MloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik05Mi4xMDk0IDExMC4wNjVMOTQuODM4NiAxMTAuMDQ2TDk0Ljg1NzMgMTEyLjgzMUw5Mi4xMjggMTEyLjg1TDkyLjEwOTQgMTEwLjA2NVpNNzUuMzMyIDExNS4yMTVMNzguMDYxMyAxMTUuMTk2TDc4LjA4IDExNy45ODFMNzUuMzUwNyAxMThMNzUuMzMyIDExNS4yMTVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNDkuNDc4NSAxMTIuODg2TDQ5LjQ5NzIgMTEwLjE1N0w1Mi4yODI1IDExMC4xNzZMNTIuMjYzOCAxMTIuOTA1TDQ5LjQ3ODUgMTEyLjg4NloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0zMC44NTk0IDk0LjE2NThMMzAuODc4MSA5MS40MzY1TDMzLjY2MzQgOTEuNDU1MkwzMy42NDQ3IDk0LjE4NDVMMzAuODU5NCA5NC4xNjU4WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTI3LjU4MDEgODYuMDE0NEwyNy41OTg4IDgzLjI4NTJMMzAuMzg0MSA4My4zMDM5TDMwLjM2NTQgODYuMDMzMUwyNy41ODAxIDg2LjAxNDRaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzUuODMyIDEwMS42NzJMMzUuODUwNyA5OC45NDI0TDM4LjYzNiA5OC45NjExTDM4LjYxNzMgMTAxLjY5TDM1LjgzMiAxMDEuNjcyWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTI2IDc3LjIzOUwyNi4wMTg3IDc0LjUwOThMMjguODA0IDc0LjUyODVMMjguNzg1MyA3Ny4yNTc3TDI2IDc3LjIzOVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik02Ni40MDUzIDExNy45NjJMNjYuNDI0IDExNS4yMzJMNjkuMjA5MyAxMTUuMjUxTDY5LjE5MDYgMTE3Ljk4TDY2LjQwNTMgMTE3Ljk2MloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik01Ny41MTc2IDExNi4xNjhMNTcuNTM2MyAxMTMuNDM4TDYwLjMyMTYgMTEzLjQ1N0w2MC4zMDI5IDExNi4xODZMNTcuNTE3NiAxMTYuMTY4WiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODNfMjk4NSIgeDE9IjEwIiB5MT0iMTUuNSIgeDI9IjE0NCIgeTI9IjEzMS41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwMEE4REEiLz4KPHN0b3Agb2Zmc2V0PSIwLjQ5NSIgc3RvcC1jb2xvcj0iIzM1NzlCQyIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMyODZFQTUiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "external"], ["spec", "httpAndHttps"], ["spec", "httpAndHttps", "mode"], ["spec", "httpAndHttps", "targetPorts"], ["spec", "httpAndHttps", "targetPorts", "http"], ["spec", "httpAndHttps", "targetPorts", "https"], ["spec", "httpAndHttps", "endpoints"], ["spec", "whitelistHTTP"], ["spec", "whitelist"]] + secrets: + exclude: [] + include: [] diff --git a/packages/system/tcp-balancer-rd/templates/cozyrd.yaml b/packages/system/tcp-balancer-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/tcp-balancer-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/tcp-balancer-rd/values.yaml b/packages/system/tcp-balancer-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/tcp-balancer-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/tenant-rd/Chart.yaml b/packages/system/tenant-rd/Chart.yaml new file mode 100644 index 00000000..ad3d27f4 --- /dev/null +++ b/packages/system/tenant-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: tenant-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/tenant-rd/Makefile b/packages/system/tenant-rd/Makefile new file mode 100644 index 00000000..37852fe6 --- /dev/null +++ b/packages/system/tenant-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=tenant-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/tenant-rd/cozyrds/tenant.yaml b/packages/system/tenant-rd/cozyrds/tenant.yaml new file mode 100644 index 00000000..a5c497ac --- /dev/null +++ b/packages/system/tenant-rd/cozyrds/tenant.yaml @@ -0,0 +1,31 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: tenant +spec: + application: + kind: Tenant + singular: tenant + plural: tenants + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"etcd":{"description":"Deploy own Etcd cluster.","type":"boolean","default":false},"host":{"description":"The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host).","type":"string","default":""},"ingress":{"description":"Deploy own Ingress Controller.","type":"boolean","default":false},"isolated":{"description":"Enforce tenant namespace with network policies (default: true).","type":"boolean","default":true},"monitoring":{"description":"Deploy own Monitoring Stack.","type":"boolean","default":false},"resourceQuotas":{"description":"Define resource quotas for the tenant.","type":"object","default":{},"additionalProperties":{"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}},"seaweedfs":{"description":"Deploy own SeaweedFS.","type":"boolean","default":false}}} + release: + prefix: tenant- + labels: + cozystack.io/ui: "true" + chart: + name: tenant + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: Administration + singular: Tenant + plural: Tenants + description: Separated tenant namespace + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODdfMzQwMykiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzY4N18zNDAzKSI+CjxwYXRoIGQ9Ik03MiAyOUM2Ni4zOTI2IDI5IDYxLjAxNDggMzEuMjM4OCA1Ny4wNDk3IDM1LjIyNEM1My4wODQ3IDM5LjIwOTEgNTAuODU3MSA0NC42MTQxIDUwLjg1NzEgNTAuMjVDNTAuODU3MSA1NS44ODU5IDUzLjA4NDcgNjEuMjkwOSA1Ny4wNDk3IDY1LjI3NkM2MS4wMTQ4IDY5LjI2MTIgNjYuMzkyNiA3MS41IDcyIDcxLjVDNzcuNjA3NCA3MS41IDgyLjk4NTIgNjkuMjYxMiA4Ni45NTAzIDY1LjI3NkM5MC45MTUzIDYxLjI5MDkgOTMuMTQyOSA1NS44ODU5IDkzLjE0MjkgNTAuMjVDOTMuMTQyOSA0NC42MTQxIDkwLjkxNTMgMzkuMjA5MSA4Ni45NTAzIDM1LjIyNEM4Mi45ODUyIDMxLjIzODggNzcuNjA3NCAyOSA3MiAyOVpNNjAuOTgyNiA4My4zMDM3QzYwLjQ1NCA4Mi41ODk4IDU5LjU5NTEgODIuMTkxNCA1OC43MTk2IDgyLjI3NDRDNDUuMzg5NyA4My43MzU0IDM1IDk1LjEwNzQgMzUgMTA4LjkwM0MzNSAxMTEuNzI2IDM3LjI3OTUgMTE0IDQwLjA3MSAxMTRIMTAzLjkyOUMxMDYuNzM3IDExNCAxMDkgMTExLjcwOSAxMDkgMTA4LjkwM0MxMDkgOTUuMTA3NCA5OC42MTAzIDgzLjc1MiA4NS4yNjM4IDgyLjI5MUM4NC4zODg0IDgyLjE5MTQgODMuNTI5NSA4Mi42MDY0IDgzLjAwMDkgODMuMzIwM0w3NC4wOTc4IDk1LjI0MDJDNzMuMDQwNiA5Ni42NTE0IDcwLjkyNjMgOTYuNjUxNCA2OS44NjkyIDk1LjI0MDJMNjAuOTY2MSA4My4zMjAzTDYwLjk4MjYgODMuMzAzN1oiIGZpbGw9ImJsYWNrIi8+CjwvZz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODdfMzQwMyIgeDE9IjcyIiB5MT0iMTQ0IiB4Mj0iLTEuMjgxN2UtMDUiIHkyPSI0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNDMEQ2RkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjMiIHN0b3AtY29sb3I9IiNDNERBRkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjY1IiBzdG9wLWNvbG9yPSIjRDNFOUZGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0U5RkZGRiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzY4N18zNDAzIj4KPHJlY3Qgd2lkdGg9Ijc0IiBoZWlnaHQ9Ijg1IiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzUgMjkpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg== + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "host"], ["spec", "etcd"], ["spec", "monitoring"], ["spec", "ingress"], ["spec", "seaweedfs"], ["spec", "isolated"], ["spec", "resourceQuotas"]] + secrets: + exclude: [] + include: [] diff --git a/packages/system/tenant-rd/templates/cozyrd.yaml b/packages/system/tenant-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/tenant-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/tenant-rd/values.yaml b/packages/system/tenant-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/tenant-rd/values.yaml @@ -0,0 +1 @@ +{} 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..2df053b5 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-vertical-pod-autoscaler-default-vpa-for-vpa + namespace: cozy-system dependsOn: - name: monitoring-agents namespace: cozy-monitoring diff --git a/packages/system/virtual-machine-rd/Chart.yaml b/packages/system/virtual-machine-rd/Chart.yaml new file mode 100644 index 00000000..6228a221 --- /dev/null +++ b/packages/system/virtual-machine-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: virtual-machine-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/virtual-machine-rd/Makefile b/packages/system/virtual-machine-rd/Makefile new file mode 100644 index 00000000..4d59946b --- /dev/null +++ b/packages/system/virtual-machine-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=virtual-machine-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml b/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml new file mode 100644 index 00000000..a1e384c4 --- /dev/null +++ b/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml @@ -0,0 +1,39 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: virtual-machine +spec: + application: + kind: VirtualMachine + plural: virtualmachines + singular: virtualmachine + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"cloudInit":{"description":"Cloud-init user data.","type":"string","default":""},"cloudInitSeed":{"description":"Seed string to generate SMBIOS UUID for the VM.","type":"string","default":""},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"externalMethod":{"description":"Method to pass through traffic to the VM.","type":"string","default":"PortList","enum":["PortList","WholeIP"]},"externalPorts":{"description":"Ports to forward from outside the cluster.","type":"array","default":[22],"items":{"type":"integer"}},"gpus":{"description":"List of GPUs to attach.","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"name":{"description":"The name of the GPU resource to attach.","type":"string"}}}},"instanceProfile":{"description":"Virtual Machine preferences profile.","type":"string","default":"ubuntu","enum":["alpine","centos.7","centos.7.desktop","centos.stream10","centos.stream10.desktop","centos.stream8","centos.stream8.desktop","centos.stream8.dpdk","centos.stream9","centos.stream9.desktop","centos.stream9.dpdk","cirros","fedora","fedora.arm64","opensuse.leap","opensuse.tumbleweed","rhel.10","rhel.10.arm64","rhel.7","rhel.7.desktop","rhel.8","rhel.8.desktop","rhel.8.dpdk","rhel.9","rhel.9.arm64","rhel.9.desktop","rhel.9.dpdk","rhel.9.realtime","sles","ubuntu","windows.10","windows.10.virtio","windows.11","windows.11.virtio","windows.2k16","windows.2k16.virtio","windows.2k19","windows.2k19.virtio","windows.2k22","windows.2k22.virtio","windows.2k25","windows.2k25.virtio",""]},"instanceType":{"description":"Virtual Machine instance type.","type":"string","default":"u1.medium"},"resources":{"description":"Resource configuration for the virtual machine.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"sockets":{"description":"Number of CPU sockets (vCPU topology).","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"running":{"description":"Whether the virtual machine should be running.","type":"boolean","default":true},"sshKeys":{"description":"List of SSH public keys for authentication.","type":"array","default":[],"items":{"type":"string"}},"subnets":{"description":"Additional subnets","type":"array","default":[],"items":{"type":"object","properties":{"name":{"description":"Subnet name","type":"string"}}}},"systemDisk":{"description":"System disk configuration.","type":"object","default":{},"required":["image","storage"],"properties":{"image":{"description":"The base image for the virtual machine.","type":"string","default":"ubuntu","enum":["ubuntu","cirros","alpine","fedora","talos"]},"storage":{"description":"The size of the disk allocated for the virtual machine.","type":"string","default":"5Gi"},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated"}}}}} + release: + prefix: virtual-machine- + labels: + cozystack.io/ui: "true" + chart: + name: virtual-machine + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: IaaS + singular: Virtual Machine + plural: Virtual Machines + description: Virtual Machine (simple) + weight: 10 + tags: + - compute + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODdfMzQ1NCkiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzY4N18zNDU0KSI+CjxwYXRoIGQ9Ik04OS41MDM5IDExMS43MDdINTQuNDk3QzU0LjE3MjcgMTExLjcwNyA1NC4wMTA4IDExMS4yMjEgNTQuMzM0OSAxMTEuMDU5TDU3LjI1MjIgMTA4Ljk1MkM2MC4zMzE0IDEwNi42ODMgNjEuOTUyMiAxMDIuNjMxIDYwLjk3OTcgOTguNzQxMkg4My4wMjFDODIuMDQ4NSAxMDIuNjMxIDgzLjY2OTMgMTA2LjY4MyA4Ni43NDg1IDEwOC45NTJMODkuNjY1OCAxMTEuMDU5Qzg5Ljk5IDExMS4yMjEgODkuODI3OSAxMTEuNzA3IDg5LjUwMzkgMTExLjcwN1oiIGZpbGw9IiNCMEI2QkIiLz4KPHBhdGggZD0iTTExMy4zMjggOTguNzQxSDMwLjY3MjVDMjcuNTkzMSA5OC43NDEgMjUgOTYuMTQ4IDI1IDkzLjA2ODdWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJWOTMuMDY4N0MxMTkgOTYuMTQ4IDExNi40MDcgOTguNzQxIDExMy4zMjggOTguNzQxWiIgZmlsbD0iI0U4RURFRSIvPgo8cGF0aCBkPSJNMTE5IDg0LjE1NDlIMjVWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJMMTE5IDg0LjE1NDlaIiBmaWxsPSIjMDBCM0ZGIi8+CjxwYXRoIGQ9Ik05MC42Mzc0IDExNi41NjlINTMuMzYxNkM1Mi4wNjUxIDExNi41NjkgNTAuOTMwNyAxMTUuNDM1IDUwLjkzMDcgMTE0LjEzOEM1MC45MzA3IDExMi44NDEgNTIuMDY1MSAxMTEuNzA3IDUzLjM2MTYgMTExLjcwN0g5MC42Mzc0QzkxLjkzMzkgMTExLjcwNyA5My4wNjg0IDExMi44NDEgOTMuMDY4NCAxMTQuMTM4QzkzLjA2ODQgMTE1LjQzNSA5MS45MzM5IDExNi41NjkgOTAuNjM3NCAxMTYuNTY5WiIgZmlsbD0iI0U4RURFRSIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi41Mjc1IDUzLjgzNjdDNzIuNDQzMSA1My44MzUxIDcyLjM2MDUgNTMuODEyMiA3Mi4yODczIDUzLjc3MDFMNTYuNDY5OSA0NC43OTM0QzU2LjM5ODMgNDQuNzUxOSA1Ni4zMzg4IDQ0LjY5MjMgNTYuMjk3MyA0NC42MjA3QzU2LjI1NTkgNDQuNTQ5IDU2LjIzMzggNDQuNDY3OCA1Ni4yMzM0IDQ0LjM4NUM1Ni4yMzM0IDQ0LjIxNjkgNTYuMzI1OCA0NC4wNjE3IDU2LjQ2OTkgNDMuOTc4NUw3Mi4xOTEyIDM1LjA2MDlDNzIuMjYzNyAzNS4wMjEgNzIuMzQ1IDM1IDcyLjQyNzcgMzVDNzIuNTEwNSAzNSA3Mi41OTE4IDM1LjAyMSA3Mi42NjQzIDM1LjA2MDlMODguNDg3MiA0NC4wMzk1Qzg4LjU1OTEgNDQuMDgwMSA4OC42MTg4IDQ0LjEzOTIgODguNjYgNDQuMjEwN0M4OC43MDEzIDQ0LjI4MjIgODguNzIyNyA0NC4zNjM1IDg4LjcyMTkgNDQuNDQ2Qzg4LjcyMjUgNDQuNTI4NSA4OC43MDEgNDQuNjA5NyA4OC42NTk4IDQ0LjY4MTJDODguNjE4NSA0NC43NTI2IDg4LjU1ODkgNDQuODExOCA4OC40ODcyIDQ0Ljg1MjVMNzIuNzcxNCA1My43NjgzQzcyLjY5NzIgNTMuODExNCA3Mi42MTMzIDUzLjgzNDkgNzIuNTI3NSA1My44MzY3IiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBvcGFjaXR5PSIwLjciIGQ9Ik03MC4yNTUzIDc1LjY1MTdDNzAuMTcxIDc1LjY1MzUgNzAuMDg3OCA3NS42MzE3IDcwLjAxNTEgNzUuNTg4OEw1NC4yNDU4IDY2LjY0MTdDNTQuMTcxNSA2Ni42MDI0IDU0LjEwOTUgNjYuNTQzNiA1NC4wNjYxIDY2LjQ3MTZDNTQuMDIyOCA2Ni4zOTk3IDU0IDY2LjMxNzMgNTQgNjYuMjMzM1Y0OC4yNzhDNTQgNDguMTA4IDU0LjA5MjQgNDcuOTU0NiA1NC4yNDM5IDQ3Ljg2OTZDNTQuMzE3MiA0Ny44MjcxIDU0LjQwMDQgNDcuODA0NyA1NC40ODUxIDQ3LjgwNDdDNTQuNTY5NyA0Ny44MDQ3IDU0LjY1MjkgNDcuODI3MSA1NC43MjYyIDQ3Ljg2OTZMNzAuNDkzNyA1Ni44MTMxQzcwLjU2NDIgNTYuODU2NSA3MC42MjI1IDU2LjkxNyA3MC42NjMyIDU2Ljk4OTFDNzAuNzAzOSA1Ny4wNjEyIDcwLjcyNTcgNTcuMTQyNCA3MC43MjY1IDU3LjIyNTFWNzUuMTgwNUM3MC43MjU5IDc1LjI2MjggNzAuNzA0MiA3NS4zNDM2IDcwLjY2MzUgNzUuNDE1MUM3MC42MjI3IDc1LjQ4NjYgNzAuNTY0MiA3NS41NDY0IDcwLjQ5MzcgNzUuNTg4OEM3MC40MjA2IDc1LjYyOTEgNzAuMzM4NyA3NS42NTA3IDcwLjI1NTMgNzUuNjUxNyIgZmlsbD0id2hpdGUiLz4KPHBhdGggb3BhY2l0eT0iMC40IiBkPSJNNzQuNzE5OCA3NS42NTExQzc0LjYzMzMgNzUuNjUxMiA3NC41NDgyIDc1LjYyOTYgNzQuNDcyMiA3NS41ODgzQzc0LjQwMTYgNzUuNTQ2MSA3NC4zNDMyIDc1LjQ4NjIgNzQuMzAyNyA3NS40MTQ3Qzc0LjI2MjMgNzUuMzQzMSA3NC4yNDExIDc1LjI2MjIgNzQuMjQxMiA3NS4xOFY1Ny4zMzczQzc0LjI0MTIgNTcuMTcxIDc0LjMzMzYgNTcuMDE1OCA3NC40NzIyIDU2LjkyOUw5MC4yMzk3IDQ3Ljk4NTVDOTAuMzExOSA0Ny45NDM4IDkwLjM5MzggNDcuOTIxOSA5MC40NzcxIDQ3LjkyMTlDOTAuNTYwNSA0Ny45MjE5IDkwLjY0MjQgNDcuOTQzOCA5MC43MTQ2IDQ3Ljk4NTVDOTAuNzg3NiA0OC4wMjU1IDkwLjg0ODUgNDguMDg0MiA5MC44OTExIDQ4LjE1NTdDOTAuOTMzNyA0OC4yMjcyIDkwLjk1NjMgNDguMzA4OCA5MC45NTY2IDQ4LjM5MlY2Ni4yMzI4QzkwLjk1NyA2Ni4zMTY0IDkwLjkzNDcgNjYuMzk4NSA5MC44OTIxIDY2LjQ3MDRDOTAuODQ5NSA2Ni41NDI0IDkwLjc4ODEgNjYuNjAxNCA5MC43MTQ2IDY2LjY0MTFMNzQuOTUyNiA3NS41ODgzQzc0Ljg4MjUgNzUuNjMwNyA3NC44MDE4IDc1LjY1MjUgNzQuNzE5OCA3NS42NTExIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4N18zNDU0IiB4MT0iMTYxIiB5MT0iMTgwIiB4Mj0iMy41OTI4NGUtMDciIHkyPSI0Ljk5OTk4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNTk1NjU2Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNjg3XzM0NTQiPgo8cmVjdCB3aWR0aD0iOTQiIGhlaWdodD0iOTQiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNSAyNSkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "external"], ["spec", "externalMethod"], ["spec", "externalPorts"], ["spec", "running"], ["spec", "instanceType"], ["spec", "instanceProfile"], ["spec", "systemDisk"], ["spec", "systemDisk", "image"], ["spec", "systemDisk", "storage"], ["spec", "systemDisk", "storageClass"], ["spec", "subnets"], ["spec", "gpus"], ["spec", "resources"], ["spec", "sshKeys"], ["spec", "cloudInit"], ["spec", "cloudInitSeed"]] + secrets: + exclude: [] + include: [] + services: + exclude: [] + include: + - resourceNames: + - virtual-machine-{{ .name }} diff --git a/packages/system/virtual-machine-rd/templates/cozyrd.yaml b/packages/system/virtual-machine-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/virtual-machine-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/virtual-machine-rd/values.yaml b/packages/system/virtual-machine-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/virtual-machine-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/virtualprivatecloud-rd/Chart.yaml b/packages/system/virtualprivatecloud-rd/Chart.yaml new file mode 100644 index 00000000..be5235dc --- /dev/null +++ b/packages/system/virtualprivatecloud-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: virtualprivatecloud-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/virtualprivatecloud-rd/Makefile b/packages/system/virtualprivatecloud-rd/Makefile new file mode 100644 index 00000000..9d9b6c50 --- /dev/null +++ b/packages/system/virtualprivatecloud-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=virtualprivatecloud-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml b/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml new file mode 100644 index 00000000..8d05a5b6 --- /dev/null +++ b/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml @@ -0,0 +1,34 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: virtualprivatecloud +spec: + application: + kind: VirtualPrivateCloud + plural: virtualprivateclouds + singular: virtualprivatecloud + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"subnets":{"description":"Subnets of a VPC","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"cidr":{"description":"IP address range","type":"string"}}}}}} + release: + prefix: "virtualprivatecloud-" + labels: + cozystack.io/ui: "true" + chart: + name: virtualprivatecloud + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: IaaS + singular: VPC + plural: VPCs + description: "Isolated networks" + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8xMDI1XzMpIi8+CjxwYXRoIGQ9Ik0xMDkuNiA4Ni4xSDExNC4zQzExNi44ODUgODYuMSAxMTkgODguMjE1IDExOSA5MC44NDdWMTA0Ljg1M0MxMTkgMTA3LjQ4NSAxMTYuODg1IDEwOS42IDExNC4zIDEwOS42SDk1LjVDOTIuOTE1IDEwOS42IDkwLjggMTA3LjQ4NSA5MC44IDEwNC44NTNWOTAuODQ3QzkwLjggODguMjE1IDkyLjkxNSA4Ni4xIDk1LjUgODYuMUgxMDAuMlY3Ni43SDc2LjdWODYuMUg4MS40QzgzLjk4NSA4Ni4xIDg2LjEgODguMjE1IDg2LjEgOTAuODQ3VjEwNC44NTNDODYuMSAxMDcuNDg1IDgzLjk4NSAxMDkuNiA4MS40IDEwOS42SDYyLjZDNjAuMDE1IDEwOS42IDU3LjkgMTA3LjQ4NSA1Ny45IDEwNC44NTNWOTAuODQ3QzU3LjkgODguMjE1IDYwLjAxNSA4Ni4xIDYyLjYgODYuMUg2Ny4zVjc2LjdINDMuOFY4Ni4xSDQ4LjVDNTEuMDg1IDg2LjEgNTMuMiA4OC4yMTUgNTMuMiA5MC44NDdWMTA0Ljg1M0M1My4yIDEwNy40ODUgNTEuMDg1IDEwOS42IDQ4LjUgMTA5LjZIMjkuN0MyNy4xMTUgMTA5LjYgMjUgMTA3LjQ4NSAyNSAxMDQuODUzVjkwLjg0N0MyNSA4OC4yMTUgMjcuMTE1IDg2LjEgMjkuNyA4Ni4xSDM0LjRWNzYuN0MzNC40IDcxLjUzIDM4LjYzIDY3LjMgNDMuOCA2Ny4zSDY3LjNWNTcuOUg2Mi42QzYwLjAxNSA1Ny45IDU3LjkgNTUuNzg1IDU3LjkgNTMuMTUzVjM5LjE0N0M1Ny45IDM2LjUxNSA2MC4wMTUgMzQuNCA2Mi42IDM0LjRIODEuNEM4My45ODUgMzQuNCA4Ni4xIDM2LjUxNSA4Ni4xIDM5LjE0N1Y1My4xNTNDODYuMSA1NS43ODUgODMuOTg1IDU3LjkgODEuNCA1Ny45SDc2LjdWNjcuM0gxMDAuMkMxMDUuMzcgNjcuMyAxMDkuNiA3MS41MyAxMDkuNiA3Ni43Vjg2LjFaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzEwMjVfMyIgeDE9IjE0Mi41IiB5MT0iMTQzIiB4Mj0iMy45OTk5OSIgeTI9IjkuNDk5OTkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzAwMDgyRSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMyRTMwNjciLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "subnets"]] + secrets: + exclude: [] + include: [] + services: + exclude: [] + include: [] diff --git a/packages/system/virtualprivatecloud-rd/templates/cozyrd.yaml b/packages/system/virtualprivatecloud-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/virtualprivatecloud-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/virtualprivatecloud-rd/values.yaml b/packages/system/virtualprivatecloud-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/virtualprivatecloud-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/vm-disk-rd/Chart.yaml b/packages/system/vm-disk-rd/Chart.yaml new file mode 100644 index 00000000..d405b0f0 --- /dev/null +++ b/packages/system/vm-disk-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: vm-disk-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/vm-disk-rd/Makefile b/packages/system/vm-disk-rd/Makefile new file mode 100644 index 00000000..e6de276d --- /dev/null +++ b/packages/system/vm-disk-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=vm-disk-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml b/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml new file mode 100644 index 00000000..c3c1b830 --- /dev/null +++ b/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml @@ -0,0 +1,34 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: vm-disk +spec: + application: + kind: VMDisk + singular: vmdisk + plural: vmdisks + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"optical":{"description":"Defines if disk should be considered optical.","type":"boolean","default":false},"source":{"description":"The source image location used to create a disk.","type":"object","default":{},"properties":{"http":{"description":"Download image from an HTTP source.","type":"object","required":["url"],"properties":{"url":{"description":"URL to download the image.","type":"string"}}},"image":{"description":"Use image by name.","type":"object","required":["name"],"properties":{"name":{"description":"Name of the image to use (uploaded as \"golden image\" or from the list: `ubuntu`, `fedora`, `cirros`, `alpine`, `talos`).","type":"string"}}},"upload":{"description":"Upload local image."}}},"storage":{"description":"The size of the disk allocated for the virtual machine.","default":"5Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated"}}} + release: + prefix: vm-disk- + labels: + cozystack.io/ui: "true" + chart: + name: vm-disk + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: IaaS + singular: VM Disk + plural: VM Disks + description: Virtual Machine disk + weight: 51 + tags: + - storage + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8zMzBfNDg5NjMpIi8+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2lfMzMwXzQ4OTYzKSI+CjxwYXRoIGQ9Ik03Mi4wMDAxIDI2LjYzNjdDNDYuODc3MiAyNi42MzY3IDI2LjUxMTIgNDcuMDAyNyAyNi41MTEyIDcyLjEyNTZDMjYuNTExMiA5Ny4yNDg0IDQ2Ljg3NzIgMTE3LjYxNCA3Mi4wMDAxIDExNy42MTRDOTcuMTIyOSAxMTcuNjE0IDExNy40ODkgOTcuMjQ4MiAxMTcuNDg5IDcyLjEyNTRDMTE3LjQ4OSA0Ny4wMDI1IDk3LjEyMjkgMjYuNjM2NyA3Mi4wMDAxIDI2LjYzNjdaTTcyLjAwMDEgODEuNjIxMkM2Ni43NTU3IDgxLjYyMTIgNjIuNTA0NCA3Ny4zNjk5IDYyLjUwNDQgNzIuMTI1NkM2Mi41MDQ0IDY2Ljg4MTIgNjYuNzU1NyA2Mi42Mjk5IDcyLjAwMDEgNjIuNjI5OUM3Ny4yNDQ0IDYyLjYyOTkgODEuNDk1NyA2Ni44ODEyIDgxLjQ5NTcgNzIuMTI1NkM4MS40OTU3IDc3LjM2OTkgNzcuMjQ0NCA4MS42MjEyIDcyLjAwMDEgODEuNjIxMloiIGZpbGw9IiNCQUQ5RkYiLz4KPC9nPgo8cGF0aCBkPSJNNTIuMDA3NSA2NS43MjA5TDMyLjI5NzYgNTkuMDQ5OEMzNi4yODQyIDQ3LjI3MTEgNDUuMzI4NSAzNy42MzE1IDU2LjQ5MSAzMy4yNjRMNjQuMDcyOCA1Mi42NDE5QzU4LjY0MiA1NC43NjY3IDU0LjAxOSA1OS43NzgzIDUyLjAwNzUgNjUuNzIwOVpNOTEuOTkyNiA3OC41M0wxMTEuNzAzIDg1LjIwMTFDMTA3LjcxNiA5Ni45Nzk5IDk4LjY3MTggMTA2LjYxOSA4Ny41MDkzIDExMC45ODdMNzkuOTI3NSA5MS42MDlDODUuMzU4MSA4OS40ODQyIDg5Ljk4MTMgODQuNDcyNiA5MS45OTI2IDc4LjUzWiIgZmlsbD0iI0VERjZGRiIvPgo8cGF0aCBkPSJNNTUuOTMyNCA1OC43MDI4QzU3LjY1MTQgNTYuNjE0IDU5LjcxOTQgNTQuODYzMiA2MS45ODk0IDUzLjYxODNDNjMuMTQ5IDUyLjk4MjQgNjQuMjYzMyA1My4xMjk2IDYzLjc4MTQgNTEuODk3OUw1Ny4yMDk1IDM1LjEwMDlDNTYuMjkwOSAzMi43NTI5IDU1LjI4MTQgMzMuNzI4NSA1My45MDYgMzQuMzg2OEM0OC45NzM0IDM2Ljc0NzYgNDQuNTM0OSA0MC4xNTkgNDAuODYwMSA0NC4zMTU0TDU1LjkzMjQgNTguNzAyOFpNODguMDY3NiA4NS41NDgxQzg1LjgzNTIgODguMjYwNSA4My4wMTQ4IDkwLjQwMjkgNzkuOTI2OSA5MS42MDhMODcuNTA4OSAxMTAuOTg2QzkzLjQ3ODUgMTA4LjY1NCA5OC44MzM2IDEwNC44MDYgMTAzLjE0IDk5LjkzNTVMODguMDY3NiA4NS41NDgxWiIgZmlsbD0iI0NERTNGRiIvPgo8cGF0aCBkPSJNNzIgODkuNDM4OEM2Mi40NTM0IDg5LjQzODggNTQuNjg2NSA4MS42NzIxIDU0LjY4NjUgNzIuMTI1NEM1NC42ODY1IDYyLjU3ODYgNjIuNDUzNCA1NC44MTE5IDcyIDU0LjgxMTlDODEuNTQ2OCA1NC44MTE5IDg5LjMxMzQgNjIuNTc4OCA4OS4zMTM0IDcyLjEyNTRDODkuMzEzNCA4MS42NzIgODEuNTQ2OCA4OS40Mzg4IDcyIDg5LjQzODhaTTcyIDU5LjExMDVDNjQuODIzNCA1OS4xMTA1IDU4Ljk4NDkgNjQuOTQ5IDU4Ljk4NDkgNzIuMTI1NkM1OC45ODQ5IDc5LjMwMjIgNjQuODIzNCA4NS4xNDA2IDcyIDg1LjE0MDZDNzkuMTc2NiA4NS4xNDA2IDg1LjAxNSA3OS4zMDIyIDg1LjAxNSA3Mi4xMjU2Qzg1LjAxNSA2NC45NDkgNzkuMTc2NiA1OS4xMTA1IDcyIDU5LjExMDVaIiBmaWxsPSIjMDBCNEZGIi8+CjxkZWZzPgo8ZmlsdGVyIGlkPSJmaWx0ZXIwX2lfMzMwXzQ4OTYzIiB4PSIyNi41MTEyIiB5PSIyNi42MzY3IiB3aWR0aD0iOTIuOTc3OCIgaGVpZ2h0PSI5Mi45Nzc1IiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9InNoYXBlIi8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPgo8ZmVPZmZzZXQgZHg9IjIiIGR5PSIyIi8+CjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjIuNSIvPgo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJhcml0aG1ldGljIiBrMj0iLTEiIGszPSIxIi8+CjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDEgMCAwIDAgMCAxIDAgMCAwIDAgMSAwIDAgMCAwLjIxIDAiLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbjI9InNoYXBlIiByZXN1bHQ9ImVmZmVjdDFfaW5uZXJTaGFkb3dfMzMwXzQ4OTYzIi8+CjwvZmlsdGVyPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMzMwXzQ4OTYzIiB4MT0iLTE1LjUiIHkxPSItMTIiIHgyPSIyMTYuNSIgeTI9IjE4NS41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiM2NUNDRkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDQ0Mzc0Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "source"], ["spec", "optical"], ["spec", "storage"], ["spec", "storageClass"]] + secrets: + exclude: [] + include: [] diff --git a/packages/system/vm-disk-rd/templates/cozyrd.yaml b/packages/system/vm-disk-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/vm-disk-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/vm-disk-rd/values.yaml b/packages/system/vm-disk-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/vm-disk-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/vm-instance-rd/Chart.yaml b/packages/system/vm-instance-rd/Chart.yaml new file mode 100644 index 00000000..270db7d6 --- /dev/null +++ b/packages/system/vm-instance-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: vm-instance-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/vm-instance-rd/Makefile b/packages/system/vm-instance-rd/Makefile new file mode 100644 index 00000000..badc4951 --- /dev/null +++ b/packages/system/vm-instance-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=vm-instance-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml b/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml new file mode 100644 index 00000000..58eb5f9a --- /dev/null +++ b/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml @@ -0,0 +1,39 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: vm-instance +spec: + application: + kind: VMInstance + singular: vminstance + plural: vminstances + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"cloudInit":{"description":"Cloud-init user data.","type":"string","default":""},"cloudInitSeed":{"description":"Seed string to generate SMBIOS UUID for the VM.","type":"string","default":""},"disks":{"description":"List of disks to attach.","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"bus":{"description":"Disk bus type (e.g. \"sata\").","type":"string"},"name":{"description":"Disk name.","type":"string"}}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"externalMethod":{"description":"Method to pass through traffic to the VM.","type":"string","default":"PortList","enum":["PortList","WholeIP"]},"externalPorts":{"description":"Ports to forward from outside the cluster.","type":"array","default":[22],"items":{"type":"integer"}},"gpus":{"description":"List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"name":{"description":"The name of the GPU resource to attach.","type":"string"}}}},"instanceProfile":{"description":"Virtual Machine preferences profile.","type":"string","default":"ubuntu","enum":["alpine","centos.7","centos.7.desktop","centos.stream10","centos.stream10.desktop","centos.stream8","centos.stream8.desktop","centos.stream8.dpdk","centos.stream9","centos.stream9.desktop","centos.stream9.dpdk","cirros","fedora","fedora.arm64","opensuse.leap","opensuse.tumbleweed","rhel.10","rhel.10.arm64","rhel.7","rhel.7.desktop","rhel.8","rhel.8.desktop","rhel.8.dpdk","rhel.9","rhel.9.arm64","rhel.9.desktop","rhel.9.dpdk","rhel.9.realtime","sles","ubuntu","windows.10","windows.10.virtio","windows.11","windows.11.virtio","windows.2k16","windows.2k16.virtio","windows.2k19","windows.2k19.virtio","windows.2k22","windows.2k22.virtio","windows.2k25","windows.2k25.virtio",""]},"instanceType":{"description":"Virtual Machine instance type.","type":"string","default":"u1.medium"},"resources":{"description":"Resource configuration for the virtual machine.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"sockets":{"description":"Number of CPU sockets (vCPU topology).","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"running":{"description":"Determines if the virtual machine should be running.","type":"boolean","default":true},"sshKeys":{"description":"List of SSH public keys for authentication.","type":"array","default":[],"items":{"type":"string"}},"subnets":{"description":"Additional subnets","type":"array","default":[],"items":{"type":"object","properties":{"name":{"description":"Subnet name","type":"string"}}}}}} + release: + prefix: vm-instance- + labels: + cozystack.io/ui: "true" + chart: + name: vm-instance + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: IaaS + singular: VM Instance + plural: VM Instances + description: Virtual machine instance + weight: 50 + tags: + - compute + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODdfMzQ1NCkiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzY4N18zNDU0KSI+CjxwYXRoIGQ9Ik04OS41MDM5IDExMS43MDdINTQuNDk3QzU0LjE3MjcgMTExLjcwNyA1NC4wMTA4IDExMS4yMjEgNTQuMzM0OSAxMTEuMDU5TDU3LjI1MjIgMTA4Ljk1MkM2MC4zMzE0IDEwNi42ODMgNjEuOTUyMiAxMDIuNjMxIDYwLjk3OTcgOTguNzQxMkg4My4wMjFDODIuMDQ4NSAxMDIuNjMxIDgzLjY2OTMgMTA2LjY4MyA4Ni43NDg1IDEwOC45NTJMODkuNjY1OCAxMTEuMDU5Qzg5Ljk5IDExMS4yMjEgODkuODI3OSAxMTEuNzA3IDg5LjUwMzkgMTExLjcwN1oiIGZpbGw9IiNCMEI2QkIiLz4KPHBhdGggZD0iTTExMy4zMjggOTguNzQxSDMwLjY3MjVDMjcuNTkzMSA5OC43NDEgMjUgOTYuMTQ4IDI1IDkzLjA2ODdWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJWOTMuMDY4N0MxMTkgOTYuMTQ4IDExNi40MDcgOTguNzQxIDExMy4zMjggOTguNzQxWiIgZmlsbD0iI0U4RURFRSIvPgo8cGF0aCBkPSJNMTE5IDg0LjE1NDlIMjVWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJMMTE5IDg0LjE1NDlaIiBmaWxsPSIjMDBCM0ZGIi8+CjxwYXRoIGQ9Ik05MC42Mzc0IDExNi41NjlINTMuMzYxNkM1Mi4wNjUxIDExNi41NjkgNTAuOTMwNyAxMTUuNDM1IDUwLjkzMDcgMTE0LjEzOEM1MC45MzA3IDExMi44NDEgNTIuMDY1MSAxMTEuNzA3IDUzLjM2MTYgMTExLjcwN0g5MC42Mzc0QzkxLjkzMzkgMTExLjcwNyA5My4wNjg0IDExMi44NDEgOTMuMDY4NCAxMTQuMTM4QzkzLjA2ODQgMTE1LjQzNSA5MS45MzM5IDExNi41NjkgOTAuNjM3NCAxMTYuNTY5WiIgZmlsbD0iI0U4RURFRSIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi41Mjc1IDUzLjgzNjdDNzIuNDQzMSA1My44MzUxIDcyLjM2MDUgNTMuODEyMiA3Mi4yODczIDUzLjc3MDFMNTYuNDY5OSA0NC43OTM0QzU2LjM5ODMgNDQuNzUxOSA1Ni4zMzg4IDQ0LjY5MjMgNTYuMjk3MyA0NC42MjA3QzU2LjI1NTkgNDQuNTQ5IDU2LjIzMzggNDQuNDY3OCA1Ni4yMzM0IDQ0LjM4NUM1Ni4yMzM0IDQ0LjIxNjkgNTYuMzI1OCA0NC4wNjE3IDU2LjQ2OTkgNDMuOTc4NUw3Mi4xOTEyIDM1LjA2MDlDNzIuMjYzNyAzNS4wMjEgNzIuMzQ1IDM1IDcyLjQyNzcgMzVDNzIuNTEwNSAzNSA3Mi41OTE4IDM1LjAyMSA3Mi42NjQzIDM1LjA2MDlMODguNDg3MiA0NC4wMzk1Qzg4LjU1OTEgNDQuMDgwMSA4OC42MTg4IDQ0LjEzOTIgODguNjYgNDQuMjEwN0M4OC43MDEzIDQ0LjI4MjIgODguNzIyNyA0NC4zNjM1IDg4LjcyMTkgNDQuNDQ2Qzg4LjcyMjUgNDQuNTI4NSA4OC43MDEgNDQuNjA5NyA4OC42NTk4IDQ0LjY4MTJDODguNjE4NSA0NC43NTI2IDg4LjU1ODkgNDQuODExOCA4OC40ODcyIDQ0Ljg1MjVMNzIuNzcxNCA1My43NjgzQzcyLjY5NzIgNTMuODExNCA3Mi42MTMzIDUzLjgzNDkgNzIuNTI3NSA1My44MzY3IiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBvcGFjaXR5PSIwLjciIGQ9Ik03MC4yNTUzIDc1LjY1MTdDNzAuMTcxIDc1LjY1MzUgNzAuMDg3OCA3NS42MzE3IDcwLjAxNTEgNzUuNTg4OEw1NC4yNDU4IDY2LjY0MTdDNTQuMTcxNSA2Ni42MDI0IDU0LjEwOTUgNjYuNTQzNiA1NC4wNjYxIDY2LjQ3MTZDNTQuMDIyOCA2Ni4zOTk3IDU0IDY2LjMxNzMgNTQgNjYuMjMzM1Y0OC4yNzhDNTQgNDguMTA4IDU0LjA5MjQgNDcuOTU0NiA1NC4yNDM5IDQ3Ljg2OTZDNTQuMzE3MiA0Ny44MjcxIDU0LjQwMDQgNDcuODA0NyA1NC40ODUxIDQ3LjgwNDdDNTQuNTY5NyA0Ny44MDQ3IDU0LjY1MjkgNDcuODI3MSA1NC43MjYyIDQ3Ljg2OTZMNzAuNDkzNyA1Ni44MTMxQzcwLjU2NDIgNTYuODU2NSA3MC42MjI1IDU2LjkxNyA3MC42NjMyIDU2Ljk4OTFDNzAuNzAzOSA1Ny4wNjEyIDcwLjcyNTcgNTcuMTQyNCA3MC43MjY1IDU3LjIyNTFWNzUuMTgwNUM3MC43MjU5IDc1LjI2MjggNzAuNzA0MiA3NS4zNDM2IDcwLjY2MzUgNzUuNDE1MUM3MC42MjI3IDc1LjQ4NjYgNzAuNTY0MiA3NS41NDY0IDcwLjQ5MzcgNzUuNTg4OEM3MC40MjA2IDc1LjYyOTEgNzAuMzM4NyA3NS42NTA3IDcwLjI1NTMgNzUuNjUxNyIgZmlsbD0id2hpdGUiLz4KPHBhdGggb3BhY2l0eT0iMC40IiBkPSJNNzQuNzE5OCA3NS42NTExQzc0LjYzMzMgNzUuNjUxMiA3NC41NDgyIDc1LjYyOTYgNzQuNDcyMiA3NS41ODgzQzc0LjQwMTYgNzUuNTQ2MSA3NC4zNDMyIDc1LjQ4NjIgNzQuMzAyNyA3NS40MTQ3Qzc0LjI2MjMgNzUuMzQzMSA3NC4yNDExIDc1LjI2MjIgNzQuMjQxMiA3NS4xOFY1Ny4zMzczQzc0LjI0MTIgNTcuMTcxIDc0LjMzMzYgNTcuMDE1OCA3NC40NzIyIDU2LjkyOUw5MC4yMzk3IDQ3Ljk4NTVDOTAuMzExOSA0Ny45NDM4IDkwLjM5MzggNDcuOTIxOSA5MC40NzcxIDQ3LjkyMTlDOTAuNTYwNSA0Ny45MjE5IDkwLjY0MjQgNDcuOTQzOCA5MC43MTQ2IDQ3Ljk4NTVDOTAuNzg3NiA0OC4wMjU1IDkwLjg0ODUgNDguMDg0MiA5MC44OTExIDQ4LjE1NTdDOTAuOTMzNyA0OC4yMjcyIDkwLjk1NjMgNDguMzA4OCA5MC45NTY2IDQ4LjM5MlY2Ni4yMzI4QzkwLjk1NyA2Ni4zMTY0IDkwLjkzNDcgNjYuMzk4NSA5MC44OTIxIDY2LjQ3MDRDOTAuODQ5NSA2Ni41NDI0IDkwLjc4ODEgNjYuNjAxNCA5MC43MTQ2IDY2LjY0MTFMNzQuOTUyNiA3NS41ODgzQzc0Ljg4MjUgNzUuNjMwNyA3NC44MDE4IDc1LjY1MjUgNzQuNzE5OCA3NS42NTExIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4N18zNDU0IiB4MT0iMTYxIiB5MT0iMTgwIiB4Mj0iMy41OTI4NGUtMDciIHkyPSI0Ljk5OTk4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNTk1NjU2Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNjg3XzM0NTQiPgo8cmVjdCB3aWR0aD0iOTQiIGhlaWdodD0iOTQiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNSAyNSkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "external"], ["spec", "externalMethod"], ["spec", "externalPorts"], ["spec", "running"], ["spec", "instanceType"], ["spec", "instanceProfile"], ["spec", "disks"], ["spec", "subnets"], ["spec", "gpus"], ["spec", "resources"], ["spec", "sshKeys"], ["spec", "cloudInit"], ["spec", "cloudInitSeed"]] + secrets: + exclude: [] + include: [] + services: + exclude: [] + include: + - matchLabels: + apps.cozystack.io/user-service: "true" diff --git a/packages/system/vm-instance-rd/templates/cozyrd.yaml b/packages/system/vm-instance-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/vm-instance-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/vm-instance-rd/values.yaml b/packages/system/vm-instance-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/vm-instance-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/packages/system/vpn-rd/Chart.yaml b/packages/system/vpn-rd/Chart.yaml new file mode 100644 index 00000000..fe0a2dd3 --- /dev/null +++ b/packages/system/vpn-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: vpn-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/vpn-rd/Makefile b/packages/system/vpn-rd/Makefile new file mode 100644 index 00000000..c6ee47bb --- /dev/null +++ b/packages/system/vpn-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=vpn-rd +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/vpn-rd/cozyrds/vpn.yaml b/packages/system/vpn-rd/cozyrds/vpn.yaml new file mode 100644 index 00000000..59d940c0 --- /dev/null +++ b/packages/system/vpn-rd/cozyrds/vpn.yaml @@ -0,0 +1,40 @@ +apiVersion: cozystack.io/v1alpha1 +kind: CozystackResourceDefinition +metadata: + name: vpn +spec: + application: + kind: VPN + plural: vpns + singular: vpn + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"externalIPs":{"description":"List of externalIPs for service. Optional. If not specified, will use LoadBalancer service by default.","type":"array","default":[],"items":{"type":"string"}},"host":{"description":"Host used to substitute into generated URLs.","type":"string","default":""},"replicas":{"description":"Number of VPN server replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each VPN server replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user (autogenerated if not provided).","type":"string"}}}}}} + release: + prefix: vpn- + labels: + cozystack.io/ui: "true" + chart: + name: vpn + sourceRef: + kind: HelmRepository + name: cozystack-apps + namespace: cozy-public + dashboard: + category: NaaS + singular: VPN + plural: VPN + description: Managed VPN service + tags: + - network + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0xNDMuOTkyIDMwLjM2OTdDMTQzLjk5MiAyOS4yNTU2IDE0My45OTIgMjguMTUxNyAxNDMuOTkyIDI3LjA0NzdDMTQzLjk0NSAyNC42Mjg3IDE0My43MTcgMjIuMjE2NiAxNDMuMzA5IDE5LjgzMTZDMTQyLjkxMiAxNy40NDczIDE0Mi4xNTcgMTUuMTM2NSAxNDEuMDcyIDEyLjk3NjlDMTM5Ljk3IDEwLjgxOCAxMzguNTM0IDguODQ2NjUgMTM2LjgxNyA3LjEzNTc3TDEzMC45OTcgMi44OTA0NEMxMjguODM3IDEuODAyODkgMTI2LjUyNyAxLjA0MTg5IDEyNC4xNDQgMC42MzIyODNDMTIxLjc1NiAwLjIzNDgwOCAxMTkuMzQgMC4wMjM0MTEzIDExNi45MTkgMEMxMTUuODE1IDAgMjguMTQ2MSAwIDI3LjAzMjMgMEMyNC42MDQ3IDAuMDI0MjIxMSAyMi4xODI2IDAuMjM1NjA5IDE5Ljc4NzYgMC42MzIyODNDMTcuNDEyOCAxLjAzODUxIDE1LjExMjYgMS43OTk3NCAxMi45NjQzIDIuODkwNDRDMTAuODEzIDMuOTk1MTkgOC44NDYyNSA1LjQyNzM2IDcuMTM0MzYgNy4xMzU3N0M1LjQxNzY4IDguODQ0NCAzLjk4NDggMTAuODE2MyAyLjg4OTg3IDEyLjk3NjlDMS44MDA4NiAxNS4xMzYgMS4wNDMxMyAxNy40NDY4IDAuNjQyMTkzIDE5LjgzMTZDMC4yNDM0OTcgMjIuMjE2OSAwLjAyODc5OTggMjQuNjI5NCAwIDI3LjA0NzdDMCAyOC4xNTE3IDAgMTE1LjgzOCAwIDExNi45NTJDMC4wMjYxNzU1IDExOS4zODQgMC4yNDA4ODQgMTIxLjgxIDAuNjQyMTkzIDEyNC4yMDlDMS4wNDA0NCAxMjYuNTk0IDEuNzk4MjkgMTI4LjkwNSAyLjg4OTg3IDEzMS4wNjNDMy45ODUxNSAxMzMuMjE4IDUuNDE4MSAxMzUuMTgzIDcuMTM0MzYgMTM2Ljg4NEM4Ljg0MDk1IDEzOC41OTkgMTAuODA4NyAxNDAuMDMyIDEyLjk2NDMgMTQxLjEzQzE1LjExNDcgMTQyLjIwOSAxNy40MTQ2IDE0Mi45NiAxOS43ODc2IDE0My4zNThDMjIuMTczMSAxNDMuNzUxIDI0LjU4NDcgMTQzLjk2NiAyNy4wMDIyIDE0NEMyOC4xMTYgMTQ0IDExNS43ODUgMTQ0IDExNi44ODkgMTQ0QzExOS4zMDMgMTQzLjk2NyAxMjEuNzEyIDE0My43NTIgMTI0LjA5NCAxNDMuMzU4QzEyNi40ODUgMTQyLjk0NiAxMjguODAxIDE0Mi4xODIgMTMwLjk2NyAxNDEuMDg5QzEzMy4xMjEgMTM5Ljk5NCAxMzUuMDg2IDEzOC41NjEgMTM2Ljc4NyAxMzYuODQ0QzEzOC41MDMgMTM1LjE0IDEzOS45MzkgMTMzLjE3NiAxNDEuMDQyIDEzMS4wMjNDMTQyLjEzNyAxMjguODc5IDE0Mi45MDEgMTI2LjU4MSAxNDMuMzA5IDEyNC4yMDlDMTQzLjcxIDEyMS44MjMgMTQzLjkzMiAxMTkuNDExIDE0My45NzIgMTE2Ljk5MkMxNDMuOTkyIDExNS44MzggMTQ0LjAxMiAzMS42NzQ0IDE0My45OTIgMzAuMzY5N1oiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODFfMjgxOCkiLz4KPHBhdGggZD0iTTExNS45NTUgNjcuNDIzMUMxMTQuOTQxIDU3LjgyMzQgMTEwLjcwMSA0OC44NTE4IDEwMy45MjggNDEuOTc1MkM5Ny4xNTQ5IDM1LjA5ODYgODguMjQ5NSAzMC43MjM5IDc4LjY2OCAyOS41NjY0VjQ2Ljc3ODZDODQuNDg4NyA0Ny45MTI3IDg5LjczMzggNTEuMDM2MiA5My41MDQ5IDU1LjYxMzdDOTcuMjc2IDYwLjE5MTIgOTkuMzM4MSA2NS45Mzc5IDk5LjMzODEgNzEuODY5MkM5OS4zMzgxIDc3LjgwMDQgOTcuMjc2IDgzLjU0NzEgOTMuNTA0OSA4OC4xMjQ2Qzg5LjczMzggOTIuNzAyMiA4NC40ODg3IDk1LjgyNTYgNzguNjY4IDk2Ljk1OThWMTE0LjIyMkM4OS43ODAxIDExMi44NzcgOTkuOTE3OCAxMDcuMjE1IDEwNi44OTQgOTguNDYwMUMxMTMuODY5IDg5LjcwNDggMTE3LjEyNCA3OC41NTczIDExNS45NTUgNjcuNDIzMVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yOC4wMTU1IDc2LjM2NTRDMjkuMDMxMSA4NS45NjQ0IDMzLjI3MTggOTQuOTM1MSA0MC4wNDQ3IDEwMS44MTFDNDYuODE3NSAxMDguNjg4IDU1LjcyMiAxMTMuMDYzIDY1LjMwMjggMTE0LjIyMlYyOS41NjY0QzU0LjE5MDcgMzAuOTExOSA0NC4wNTMgMzYuNTczMSAzNy4wNzcyIDQ1LjMyODRDMzAuMTAxMyA1NC4wODM3IDI2Ljg0NjcgNjUuMjMxMiAyOC4wMTU1IDc2LjM2NTRaIiBmaWxsPSIjNUJCMTkzIi8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfNjgxXzI4MTgiIHgxPSIxMzIuNSIgeTE9IjEzMi41IiB4Mj0iLTI0IiB5Mj0iLTE5IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMxODM3MjkiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNDU5RDc1Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "external"], ["spec", "host"], ["spec", "users"], ["spec", "externalIPs"]] + secrets: + exclude: [] + include: + - resourceNames: + - vpn-{{ .name }}-urls + services: + exclude: [] + include: + - resourceNames: + - vpn-{{ .name }}-vpn diff --git a/packages/system/vpn-rd/templates/cozyrd.yaml b/packages/system/vpn-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/vpn-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/vpn-rd/values.yaml b/packages/system/vpn-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/vpn-rd/values.yaml @@ -0,0 +1 @@ +{} diff --git a/pkg/apis/apps/v1alpha1/types.go b/pkg/apis/apps/v1alpha1/types.go index 8e756779..0118b0de 100644 --- a/pkg/apis/apps/v1alpha1/types.go +++ b/pkg/apis/apps/v1alpha1/types.go @@ -21,6 +21,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +// Application label keys used to identify and filter HelmReleases +const ( + ApplicationKindLabel = "apps.cozystack.io/application.kind" + ApplicationGroupLabel = "apps.cozystack.io/application.group" + ApplicationNameLabel = "apps.cozystack.io/application.name" +) + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // ApplicationList is a list of Application objects. diff --git a/pkg/cmd/server/start.go b/pkg/cmd/server/start.go index 3427e61d..d9566498 100644 --- a/pkg/cmd/server/start.go +++ b/pkg/cmd/server/start.go @@ -35,15 +35,9 @@ 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" - 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" k8sconfig "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -87,7 +81,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 +101,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 } @@ -225,7 +189,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,13 +244,6 @@ 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, - ) - if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil { return nil, err } @@ -318,18 +274,3 @@ func (o CozyServerOptions) RunCozyServer(ctx context.Context) error { return server.GenericAPIServer.PrepareRun().RunWithContext(ctx) } - -// CozyVersionToKubeVersion defines the version mapping between the Cozy component and kube -func CozyVersionToKubeVersion(ver *version.Version) *version.Version { - if ver.Major() != 1 { - return nil - } - kubeVer := utilversionpkg.DefaultKubeEffectiveVersion().BinaryVersion() - // "1.2" corresponds to kubeVer - offset := int(ver.Minor()) - 2 - mappedVer := kubeVer.OffsetMinor(offset) - if mappedVer.GreaterThan(kubeVer) { - return kubeVer - } - return mappedVer -} diff --git a/pkg/cmd/server/start_test.go b/pkg/cmd/server/start_test.go index da96ed31..df7c3547 100644 --- a/pkg/cmd/server/start_test.go +++ b/pkg/cmd/server/start_test.go @@ -16,53 +16,5 @@ limitations under the License. package server -import ( - "testing" - - "k8s.io/apimachinery/pkg/util/version" - utilversion "k8s.io/apiserver/pkg/util/version" - - "github.com/stretchr/testify/assert" -) - -func TestCozyEmulationVersionToKubeEmulationVersion(t *testing.T) { - defaultKubeEffectiveVersion := utilversion.DefaultKubeEffectiveVersion() - - testCases := []struct { - desc string - appsEmulationVer *version.Version - expectedKubeEmulationVer *version.Version - }{ - { - desc: "same version as than kube binary", - appsEmulationVer: version.MajorMinor(1, 2), - expectedKubeEmulationVer: defaultKubeEffectiveVersion.BinaryVersion(), - }, - { - desc: "1 version lower than kube binary", - appsEmulationVer: version.MajorMinor(1, 1), - expectedKubeEmulationVer: defaultKubeEffectiveVersion.BinaryVersion().OffsetMinor(-1), - }, - { - desc: "2 versions lower than kube binary", - appsEmulationVer: version.MajorMinor(1, 0), - expectedKubeEmulationVer: defaultKubeEffectiveVersion.BinaryVersion().OffsetMinor(-2), - }, - { - desc: "capped at kube binary", - appsEmulationVer: version.MajorMinor(1, 3), - expectedKubeEmulationVer: defaultKubeEffectiveVersion.BinaryVersion(), - }, - { - desc: "no mapping", - appsEmulationVer: version.MajorMinor(2, 10), - }, - } - - for _, tc := range testCases { - t.Run(tc.desc, func(t *testing.T) { - mappedKubeEmulationVer := CozyVersionToKubeVersion(tc.appsEmulationVer) - assert.True(t, mappedKubeEmulationVer.EqualTo(tc.expectedKubeEmulationVer)) - }) - } -} +// Note: Tests for KEP-4330 component versioning functionality have been removed +// as the functionality is not available in Kubernetes v0.34.1. 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/registry/apps/application/rest.go b/pkg/registry/apps/application/rest.go index 704cd709..78ff8dbc 100644 --- a/pkg/registry/apps/application/rest.go +++ b/pkg/registry/apps/application/rest.go @@ -32,6 +32,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" @@ -66,6 +67,13 @@ const ( AnnotationPrefix = "apps.cozystack.io-" ) +// Application label keys - use constants from API package +const ( + ApplicationKindLabel = appsv1alpha1.ApplicationKindLabel + ApplicationGroupLabel = appsv1alpha1.ApplicationGroupLabel + ApplicationNameLabel = appsv1alpha1.ApplicationNameLabel +) + // Define the GroupVersionResource for HelmRelease var helmReleaseGVR = schema.GroupVersionResource{ Group: "helm.toolkit.fluxcd.io", @@ -157,6 +165,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[ApplicationKindLabel] = r.kindName + helmRelease.Labels[ApplicationGroupLabel] = r.gvk.Group + helmRelease.Labels[ApplicationNameLabel] = 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) @@ -208,9 +223,9 @@ 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 !r.hasRequiredApplicationLabels(helmRelease) { + 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) } @@ -266,6 +281,19 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption } // Process label.selector + // Always add application metadata label requirements + appKindReq, err := labels.NewRequirement(ApplicationKindLabel, 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(ApplicationGroupLabel, 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) @@ -285,9 +313,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{ @@ -306,18 +337,19 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption return nil, err } + klog.V(6).Infof("Found %d HelmReleases with label selector", len(hrList.Items)) + // Initialize Application items array items := make([]appsv1alpha1.Application, 0, len(hrList.Items)) // Iterate over HelmReleases and convert to Applications + // Note: All HelmReleases already match the required labels due to server-side label selector filtering for i := range hrList.Items { - if !r.shouldIncludeHelmRelease(&hrList.Items[i]) { - continue - } + hr := &hrList.Items[i] - 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 } @@ -436,18 +468,17 @@ 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[ApplicationKindLabel] = r.kindName + helmRelease.Labels[ApplicationGroupLabel] = r.gvk.Group + helmRelease.Labels[ApplicationNameLabel] = 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 - // 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) - // Return a NotFound error for the Application resource - return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name) - } - // Update the HelmRelease in Kubernetes err = r.c.Update(ctx, helmRelease, &client.UpdateOptions{Raw: &metav1.UpdateOptions{}}) if err != nil { @@ -455,13 +486,6 @@ 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()) - // Return a NotFound error for the Application resource - return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name) - } - // Convert the updated HelmRelease back to Application convertedApp, err := r.ConvertHelmReleaseToApplication(helmRelease) if err != nil { @@ -503,9 +527,9 @@ func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.Va return nil, false, err } - // Validate that the HelmRelease meets the inclusion criteria - if !r.shouldIncludeHelmRelease(helmRelease) { - klog.Errorf("HelmRelease %s does not match the required chartName and sourceRef criteria", helmReleaseName) + // Validate that the HelmRelease has required labels + if !r.hasRequiredApplicationLabelsWithName(helmRelease, name) { + klog.Errorf("HelmRelease %s does not match the required application labels", helmReleaseName) // Return NotFound error for Application resource return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name) } @@ -523,7 +547,7 @@ func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.Va return nil, true, nil } -// Watch sets up a watch on HelmReleases, filters them based on sourceRef and prefix, and converts events to Applications +// Watch sets up a watch on HelmReleases, filters them based on application labels, and converts events to Applications func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { namespace, err := r.getNamespace(ctx) if err != nil { @@ -564,6 +588,19 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio } // Process label.selector + // Always add application metadata label requirements + appKindReq, err := labels.NewRequirement(ApplicationKindLabel, 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(ApplicationGroupLabel, 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) @@ -583,9 +620,10 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio } prefixedReqs = append(prefixedReqs, *prefixedReq) } - helmLabelSelector = labels.NewSelector().Add(prefixedReqs...).String() + labelRequirements = append(labelRequirements, prefixedReqs...) } } + helmLabelSelector = labels.NewSelector().Add(labelRequirements...).String() // Set ListOptions for HelmRelease with selector mapping metaOptions := metav1.ListOptions{ @@ -639,10 +677,7 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio continue } - if !r.shouldIncludeHelmRelease(hr) { - continue - } - + // Note: All HelmReleases already match the required labels due to server-side label selector filtering // Convert HelmRelease to Application app, err := r.ConvertHelmReleaseToApplication(hr) if err != nil { @@ -694,14 +729,6 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio return customW, nil } -// Helper function to get HelmRelease name from object -func helmReleaseName(obj runtime.Object) string { - if app, ok := obj.(*appsv1alpha1.Application); ok { - return app.GetName() - } - return "" -} - // customWatcher wraps the original watcher and filters/converts events type customWatcher struct { resultChan chan watch.Event @@ -725,74 +752,6 @@ func (cw *customWatcher) ResultChan() <-chan watch.Event { return cw.resultChan } -// 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()) - 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 -func (r *REST) matchesSourceRefAndPrefix(hr *helmv2.HelmRelease) bool { - // Nil check for Chart field (defensive) - if hr.Spec.Chart == nil { - klog.V(6).Infof("HelmRelease %s has nil spec.chart field", hr.GetName()) - return false - } - - // Extract SourceRef fields - sourceRef := hr.Spec.Chart.Spec.SourceRef - sourceRefKind := sourceRef.Kind - sourceRefName := sourceRef.Name - sourceRefNamespace := sourceRef.Namespace - - if sourceRefKind == "" { - klog.V(6).Infof("HelmRelease %s missing spec.chart.spec.sourceRef.kind field", hr.GetName()) - return false - } - if sourceRefName == "" { - klog.V(6).Infof("HelmRelease %s missing spec.chart.spec.sourceRef.name field", hr.GetName()) - return false - } - if sourceRefNamespace == "" { - klog.V(6).Infof("HelmRelease %s missing spec.chart.spec.sourceRef.namespace field", hr.GetName()) - return false - } - - // Check if SourceRef matches the configuration - if sourceRefKind != r.releaseConfig.Chart.SourceRef.Kind || - sourceRefName != r.releaseConfig.Chart.SourceRef.Name || - sourceRefNamespace != r.releaseConfig.Chart.SourceRef.Namespace { - klog.V(6).Infof("HelmRelease %s 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) @@ -804,13 +763,25 @@ func (r *REST) getNamespace(ctx context.Context) (string, error) { return namespace, nil } -// buildLabelSelector constructs a label selector string from a map of labels -func buildLabelSelector(labels map[string]string) string { - var selectors []string - for k, v := range labels { - selectors = append(selectors, fmt.Sprintf("%s=%s", k, v)) +// hasRequiredApplicationLabels checks if a HelmRelease has the required application labels +// matching the REST instance's kind and group +func (r *REST) hasRequiredApplicationLabels(hr *helmv2.HelmRelease) bool { + if hr.Labels == nil { + return false } - return strings.Join(selectors, ",") + return hr.Labels[ApplicationKindLabel] == r.kindName && + hr.Labels[ApplicationGroupLabel] == r.gvk.Group +} + +// hasRequiredApplicationLabelsWithName checks if a HelmRelease has the required application labels +// matching the REST instance's kind, group, and the specified application name +func (r *REST) hasRequiredApplicationLabelsWithName(hr *helmv2.HelmRelease, appName string) bool { + if hr.Labels == nil { + return false + } + return hr.Labels[ApplicationKindLabel] == r.kindName && + hr.Labels[ApplicationGroupLabel] == r.gvk.Group && + hr.Labels[ApplicationNameLabel] == appName } // mergeMaps combines two maps of labels or annotations diff --git a/scripts/migrations/22 b/scripts/migrations/22 new file mode 100755 index 00000000..192e431c --- /dev/null +++ b/scripts/migrations/22 @@ -0,0 +1,163 @@ +#!/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") + status=$? + if [ $status -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- +