[cozystack-operator] Introduce API objects: packages and packagesources

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
This commit is contained in:
Andrei Kvapil 2025-12-22 08:48:54 +01:00
parent fe7bdcf06b
commit 98488100e4
No known key found for this signature in database
GPG key ID: 931CF7FEACEAF765
9 changed files with 1048 additions and 13 deletions

View file

@ -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

View file

@ -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"`
}
// PackageComponent 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"`
}

View file

@ -0,0 +1,171 @@
/*
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"`
}
// 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"`
}

View file

@ -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,277 @@ 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 *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 +615,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

View file

@ -24,7 +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)
COZY_CONTROLLER_CRDDIR=packages/system/cozystack-controller/crds
OPERATOR_CRDDIR=packages/core/installer/definitions
COZY_CONTROLLER_CRDDIR=packages/system/cozystack-controller/definitions
COZY_RD_CRDDIR=packages/system/cozystack-resource-definition-crd/definition
BACKUPS_CORE_CRDDIR=packages/system/backup-controller/definitions
BACKUPSTRATEGY_CRDDIR=packages/system/backupstrategy-controller/definitions
@ -62,6 +63,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

View file

@ -0,0 +1 @@
*.yaml linguist-generated

View file

@ -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: PackageComponent 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: {}

View file

@ -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: {}

View file

@ -0,0 +1,6 @@
{{/*
{{- range $path, $_ := .Files.Glob "definitions/*.yaml" }}
---
{{ $.Files.Get $path }}
{{- end }}
*/}}