[cozystack-api] Implement TenantNamespace, TenantModules, TenantSecret and TenantSecretsTable resources

[cozystack-controller] Introduce new dashboard-controller
[dashboard] Introduce new dashboard based on openapi-ui

Co-authored-by: kklinch0 <kklinch0@gmail.com>
Signed-off-by: kklinch0 <kklinch0@gmail.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
This commit is contained in:
Andrei Kvapil
2025-09-24 11:33:27 +02:00
parent 789666d53b
commit 0afc3c1e86
368 changed files with 15196 additions and 24782 deletions

View File

@@ -5,7 +5,7 @@ repos:
name: Run 'make generate' in all app directories
entry: |
flock -x .git/pre-commit.lock sh -c '
for dir in ./packages/apps/*/ ./packages/extra/*/ ./packages/system/cozystack-api/; do
for dir in ./packages/apps/*/ ./packages/extra/*/; do
if [ -d "$dir" ]; then
echo "Running make generate in $dir"
make generate -C "$dir" || exit $?

View File

@@ -1,4 +1,5 @@
API rule violation: list_type_missing,github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1,ApplicationStatus,Conditions
API rule violation: list_type_missing,github.com/cozystack/cozystack/pkg/apis/core/v1alpha1,TenantModuleStatus,Conditions
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,Ref
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,Schema
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,XEmbeddedResource

View File

@@ -0,0 +1,255 @@
// SPDX-License-Identifier: Apache-2.0
// Package v1alpha1 defines front.in-cloud.io API types.
//
// Group: dashboard.cozystack.io
// Version: v1alpha1
package v1alpha1
import (
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// -----------------------------------------------------------------------------
// Shared shapes
// -----------------------------------------------------------------------------
// CommonStatus is a generic Status block with Kubernetes conditions.
type CommonStatus struct {
// ObservedGeneration reflects the most recent generation observed by the controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// Conditions represent the latest available observations of an object's state.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
// ArbitrarySpec holds schemaless user data and preserves unknown fields.
// We map the entire .spec to a single JSON payload to mirror the CRDs you provided.
// NOTE: Using apiextensionsv1.JSON avoids losing arbitrary structure during round-trips.
type ArbitrarySpec struct {
// +kubebuilder:validation:XPreserveUnknownFields
// +kubebuilder:pruning:PreserveUnknownFields
v1.JSON `json:",inline"`
}
// -----------------------------------------------------------------------------
// Sidebar
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=sidebars,scope=Cluster
// +kubebuilder:subresource:status
type Sidebar struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type SidebarList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Sidebar `json:"items"`
}
// -----------------------------------------------------------------------------
// CustomFormsPrefill (shortName: cfp)
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=customformsprefills,scope=Cluster,shortName=cfp
// +kubebuilder:subresource:status
type CustomFormsPrefill struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type CustomFormsPrefillList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CustomFormsPrefill `json:"items"`
}
// -----------------------------------------------------------------------------
// BreadcrumbInside
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=breadcrumbsinside,scope=Cluster
// +kubebuilder:subresource:status
type BreadcrumbInside struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type BreadcrumbInsideList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []BreadcrumbInside `json:"items"`
}
// -----------------------------------------------------------------------------
// CustomFormsOverride (shortName: cfo)
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=customformsoverrides,scope=Cluster,shortName=cfo
// +kubebuilder:subresource:status
type CustomFormsOverride struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type CustomFormsOverrideList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CustomFormsOverride `json:"items"`
}
// -----------------------------------------------------------------------------
// TableUriMapping
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=tableurimappings,scope=Cluster
// +kubebuilder:subresource:status
type TableUriMapping struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type TableUriMappingList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []TableUriMapping `json:"items"`
}
// -----------------------------------------------------------------------------
// Breadcrumb
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=breadcrumbs,scope=Cluster
// +kubebuilder:subresource:status
type Breadcrumb struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type BreadcrumbList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Breadcrumb `json:"items"`
}
// -----------------------------------------------------------------------------
// MarketplacePanel
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=marketplacepanels,scope=Cluster
// +kubebuilder:subresource:status
type MarketplacePanel struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type MarketplacePanelList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []MarketplacePanel `json:"items"`
}
// -----------------------------------------------------------------------------
// Navigation
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=navigations,scope=Cluster
// +kubebuilder:subresource:status
type Navigation struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type NavigationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Navigation `json:"items"`
}
// -----------------------------------------------------------------------------
// CustomColumnsOverride
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=customcolumnsoverrides,scope=Cluster
// +kubebuilder:subresource:status
type CustomColumnsOverride struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type CustomColumnsOverrideList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CustomColumnsOverride `json:"items"`
}
// -----------------------------------------------------------------------------
// Factory
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=factories,scope=Cluster
// +kubebuilder:subresource:status
type Factory struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type FactoryList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Factory `json:"items"`
}

View File

@@ -0,0 +1,75 @@
/*
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 contains API Schema definitions for the v1alpha1 API group.
// +kubebuilder:object:generate=true
// +groupName=dashboard.cozystack.io
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var (
// GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "dashboard.cozystack.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
GroupVersion,
&Sidebar{},
&SidebarList{},
&CustomFormsPrefill{},
&CustomFormsPrefillList{},
&BreadcrumbInside{},
&BreadcrumbInsideList{},
&CustomFormsOverride{},
&CustomFormsOverrideList{},
&TableUriMapping{},
&TableUriMappingList{},
&Breadcrumb{},
&BreadcrumbList{},
&MarketplacePanel{},
&MarketplacePanelList{},
&Navigation{},
&NavigationList{},
&CustomColumnsOverride{},
&CustomColumnsOverrideList{},
&Factory{},
&FactoryList{},
)
metav1.AddToGroupVersion(scheme, GroupVersion)
return nil
}

View File

@@ -0,0 +1,654 @@
//go:build !ignore_autogenerated
/*
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.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
import (
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ArbitrarySpec) DeepCopyInto(out *ArbitrarySpec) {
*out = *in
in.JSON.DeepCopyInto(&out.JSON)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArbitrarySpec.
func (in *ArbitrarySpec) DeepCopy() *ArbitrarySpec {
if in == nil {
return nil
}
out := new(ArbitrarySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Breadcrumb) DeepCopyInto(out *Breadcrumb) {
*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 Breadcrumb.
func (in *Breadcrumb) DeepCopy() *Breadcrumb {
if in == nil {
return nil
}
out := new(Breadcrumb)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Breadcrumb) 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 *BreadcrumbInside) DeepCopyInto(out *BreadcrumbInside) {
*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 BreadcrumbInside.
func (in *BreadcrumbInside) DeepCopy() *BreadcrumbInside {
if in == nil {
return nil
}
out := new(BreadcrumbInside)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BreadcrumbInside) 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 *BreadcrumbInsideList) DeepCopyInto(out *BreadcrumbInsideList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]BreadcrumbInside, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BreadcrumbInsideList.
func (in *BreadcrumbInsideList) DeepCopy() *BreadcrumbInsideList {
if in == nil {
return nil
}
out := new(BreadcrumbInsideList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BreadcrumbInsideList) 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 *BreadcrumbList) DeepCopyInto(out *BreadcrumbList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Breadcrumb, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BreadcrumbList.
func (in *BreadcrumbList) DeepCopy() *BreadcrumbList {
if in == nil {
return nil
}
out := new(BreadcrumbList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BreadcrumbList) 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 *CommonStatus) DeepCopyInto(out *CommonStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonStatus.
func (in *CommonStatus) DeepCopy() *CommonStatus {
if in == nil {
return nil
}
out := new(CommonStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomColumnsOverride) DeepCopyInto(out *CustomColumnsOverride) {
*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 CustomColumnsOverride.
func (in *CustomColumnsOverride) DeepCopy() *CustomColumnsOverride {
if in == nil {
return nil
}
out := new(CustomColumnsOverride)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomColumnsOverride) 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 *CustomColumnsOverrideList) DeepCopyInto(out *CustomColumnsOverrideList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomColumnsOverride, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomColumnsOverrideList.
func (in *CustomColumnsOverrideList) DeepCopy() *CustomColumnsOverrideList {
if in == nil {
return nil
}
out := new(CustomColumnsOverrideList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomColumnsOverrideList) 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 *CustomFormsOverride) DeepCopyInto(out *CustomFormsOverride) {
*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 CustomFormsOverride.
func (in *CustomFormsOverride) DeepCopy() *CustomFormsOverride {
if in == nil {
return nil
}
out := new(CustomFormsOverride)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomFormsOverride) 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 *CustomFormsOverrideList) DeepCopyInto(out *CustomFormsOverrideList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomFormsOverride, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomFormsOverrideList.
func (in *CustomFormsOverrideList) DeepCopy() *CustomFormsOverrideList {
if in == nil {
return nil
}
out := new(CustomFormsOverrideList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomFormsOverrideList) 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 *CustomFormsPrefill) DeepCopyInto(out *CustomFormsPrefill) {
*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 CustomFormsPrefill.
func (in *CustomFormsPrefill) DeepCopy() *CustomFormsPrefill {
if in == nil {
return nil
}
out := new(CustomFormsPrefill)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomFormsPrefill) 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 *CustomFormsPrefillList) DeepCopyInto(out *CustomFormsPrefillList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomFormsPrefill, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomFormsPrefillList.
func (in *CustomFormsPrefillList) DeepCopy() *CustomFormsPrefillList {
if in == nil {
return nil
}
out := new(CustomFormsPrefillList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomFormsPrefillList) 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 *Factory) DeepCopyInto(out *Factory) {
*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 Factory.
func (in *Factory) DeepCopy() *Factory {
if in == nil {
return nil
}
out := new(Factory)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Factory) 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 *FactoryList) DeepCopyInto(out *FactoryList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Factory, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FactoryList.
func (in *FactoryList) DeepCopy() *FactoryList {
if in == nil {
return nil
}
out := new(FactoryList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FactoryList) 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 *MarketplacePanel) DeepCopyInto(out *MarketplacePanel) {
*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 MarketplacePanel.
func (in *MarketplacePanel) DeepCopy() *MarketplacePanel {
if in == nil {
return nil
}
out := new(MarketplacePanel)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MarketplacePanel) 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 *MarketplacePanelList) DeepCopyInto(out *MarketplacePanelList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MarketplacePanel, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MarketplacePanelList.
func (in *MarketplacePanelList) DeepCopy() *MarketplacePanelList {
if in == nil {
return nil
}
out := new(MarketplacePanelList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MarketplacePanelList) 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 *Navigation) DeepCopyInto(out *Navigation) {
*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 Navigation.
func (in *Navigation) DeepCopy() *Navigation {
if in == nil {
return nil
}
out := new(Navigation)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Navigation) 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 *NavigationList) DeepCopyInto(out *NavigationList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Navigation, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NavigationList.
func (in *NavigationList) DeepCopy() *NavigationList {
if in == nil {
return nil
}
out := new(NavigationList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NavigationList) 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 *Sidebar) DeepCopyInto(out *Sidebar) {
*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 Sidebar.
func (in *Sidebar) DeepCopy() *Sidebar {
if in == nil {
return nil
}
out := new(Sidebar)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Sidebar) 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 *SidebarList) DeepCopyInto(out *SidebarList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Sidebar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidebarList.
func (in *SidebarList) DeepCopy() *SidebarList {
if in == nil {
return nil
}
out := new(SidebarList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *SidebarList) 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 *TableUriMapping) DeepCopyInto(out *TableUriMapping) {
*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 TableUriMapping.
func (in *TableUriMapping) DeepCopy() *TableUriMapping {
if in == nil {
return nil
}
out := new(TableUriMapping)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TableUriMapping) 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 *TableUriMappingList) DeepCopyInto(out *TableUriMappingList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]TableUriMapping, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TableUriMappingList.
func (in *TableUriMappingList) DeepCopy() *TableUriMappingList {
if in == nil {
return nil
}
out := new(TableUriMappingList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TableUriMappingList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@@ -21,6 +21,7 @@ import (
)
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster
// CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions API
type CozystackResourceDefinition struct {
@@ -48,8 +49,12 @@ type CozystackResourceDefinitionSpec struct {
Application CozystackResourceDefinitionApplication `json:"application"`
// Release configuration
Release CozystackResourceDefinitionRelease `json:"release"`
// Secret selectors
Secrets CozystackResourceDefinitionSecrets `json:"secrets,omitempty"`
// Dashboard configuration for this resource
Dashboard *CozystackResourceDefinitionDashboard `json:"dashboard,omitempty"`
}
type CozystackResourceDefinitionChart struct {
@@ -101,3 +106,51 @@ type CozystackResourceDefinitionSecrets struct {
// as a tenant secret and is visible to users.
Include []*metav1.LabelSelector `json:"include,omitempty"`
}
// ---- Dashboard types ----
// DashboardTab enumerates allowed UI tabs.
// +kubebuilder:validation:Enum=workloads;ingresses;services;secrets;yaml
type DashboardTab string
const (
DashboardTabWorkloads DashboardTab = "workloads"
DashboardTabIngresses DashboardTab = "ingresses"
DashboardTabServices DashboardTab = "services"
DashboardTabSecrets DashboardTab = "secrets"
DashboardTabYAML DashboardTab = "yaml"
)
// CozystackResourceDefinitionDashboard describes how this resource appears in the UI.
type CozystackResourceDefinitionDashboard struct {
// Human-readable name shown in the UI (e.g., "Bucket")
Singular string `json:"singular"`
// Plural human-readable name (e.g., "Buckets")
Plural string `json:"plural"`
// Hard-coded name used in the UI (e.g., "bucket")
// +optional
Name string `json:"name,omitempty"`
// Whether this resource is singular (not a collection) in the UI
// +optional
SingularResource bool `json:"singularResource,omitempty"`
// Order weight for sorting resources in the UI (lower first)
// +optional
Weight int `json:"weight,omitempty"`
// Short description shown in catalogs or headers (e.g., "S3 compatible storage")
// +optional
Description string `json:"description,omitempty"`
// Icon encoded as a string (e.g., inline SVG, base64, or data URI)
// +optional
Icon string `json:"icon,omitempty"`
// Category used to group resources in the UI (e.g., "Storage", "Networking")
Category string `json:"category"`
// Free-form tags for search and filtering
// +optional
Tags []string `json:"tags,omitempty"`
// Which tabs to show for this resource
// +optional
Tabs []DashboardTab `json:"tabs,omitempty"`
// Order of keys in the YAML view
// +optional
KeysOrder [][]string `json:"keysOrder,omitempty"`
}

View File

@@ -83,6 +83,42 @@ func (in *CozystackResourceDefinitionChart) DeepCopy() *CozystackResourceDefinit
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionDashboard) DeepCopyInto(out *CozystackResourceDefinitionDashboard) {
*out = *in
if in.Tags != nil {
in, out := &in.Tags, &out.Tags
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Tabs != nil {
in, out := &in.Tabs, &out.Tabs
*out = make([]DashboardTab, len(*in))
copy(*out, *in)
}
if in.KeysOrder != nil {
in, out := &in.KeysOrder, &out.KeysOrder
*out = make([][]string, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = make([]string, len(*in))
copy(*out, *in)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionDashboard.
func (in *CozystackResourceDefinitionDashboard) DeepCopy() *CozystackResourceDefinitionDashboard {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionDashboard)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionList) DeepCopyInto(out *CozystackResourceDefinitionList) {
*out = *in
@@ -181,6 +217,11 @@ func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDe
out.Application = in.Application
in.Release.DeepCopyInto(&out.Release)
in.Secrets.DeepCopyInto(&out.Secrets)
if in.Dashboard != nil {
in, out := &in.Dashboard, &out.Dashboard
*out = new(CozystackResourceDefinitionDashboard)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionSpec.

View File

@@ -26,8 +26,8 @@ import (
func main() {
ctx := genericapiserver.SetupSignalContext()
options := server.NewAppsServerOptions(os.Stdout, os.Stderr)
cmd := server.NewCommandStartAppsServer(ctx, options)
options := server.NewCozyServerOptions(os.Stdout, os.Stderr)
cmd := server.NewCommandStartCozyServer(ctx, options)
code := cli.Run(cmd)
os.Exit(code)
}

View File

@@ -38,6 +38,7 @@ import (
cozystackiov1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
"github.com/cozystack/cozystack/internal/controller"
"github.com/cozystack/cozystack/internal/controller/dashboard"
lcw "github.com/cozystack/cozystack/internal/lineagecontrollerwebhook"
"github.com/cozystack/cozystack/internal/telemetry"
@@ -54,6 +55,7 @@ func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(cozystackiov1alpha1.AddToScheme(scheme))
utilruntime.Must(dashboard.AddToScheme(scheme))
utilruntime.Must(helmv2.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}

139
hack/update-crd.sh Executable file
View File

@@ -0,0 +1,139 @@
#!/usr/bin/env bash
set -euo pipefail
# Requirements: yq (v4), jq, base64
need() { command -v "$1" >/dev/null 2>&1 || { echo "need $1"; exit 1; }; }
need yq; need jq; need base64
CHART_YAML="${CHART_YAML:-Chart.yaml}"
VALUES_YAML="${VALUES_YAML:-values.yaml}"
SCHEMA_JSON="${SCHEMA_JSON:-values.schema.json}"
CRD_DIR="../../system/cozystack-api/templates/cozystack-resource-definitions"
[[ -f "$CHART_YAML" ]] || { echo "No $CHART_YAML found"; exit 1; }
[[ -f "$SCHEMA_JSON" ]] || { echo "No $SCHEMA_JSON found"; exit 1; }
# Read basics from Chart.yaml
NAME="$(yq -r '.name // ""' "$CHART_YAML")"
DESC="$(yq -r '.description // ""' "$CHART_YAML")"
ICON_PATH_RAW="$(yq -r '.icon // ""' "$CHART_YAML")"
if [[ -z "$NAME" ]]; then
echo "Chart.yaml: .name is empty"; exit 1
fi
# Resolve icon path
# Accepts:
# /logos/foo.svg -> ./logos/foo.svg
# logos/foo.svg -> logos/foo.svg
# ./logos/foo.svg -> ./logos/foo.svg
# Fallback: ./logos/${NAME}.svg
resolve_icon_path() {
local p="$1"
if [[ -z "$p" || "$p" == "null" ]]; then
echo "./logos/${NAME}.svg"; return
fi
if [[ "$p" == /* ]]; then
echo ".${p}"
else
echo "$p"
fi
}
ICON_PATH="$(resolve_icon_path "$ICON_PATH_RAW")"
if [[ ! -f "$ICON_PATH" ]]; then
# try fallback
ALT="./logos/${NAME}.svg"
if [[ -f "$ALT" ]]; then
ICON_PATH="$ALT"
else
echo "Icon not found: $ICON_PATH"; exit 1
fi
fi
# Base64 (portable: no -w / -b options)
ICON_B64="$(base64 < "$ICON_PATH" | tr -d '\n' | tr -d '\r')"
# Decide which HelmRepository name to use based on path
# .../apps/... -> cozystack-apps
# .../extra/... -> cozystack-extra
# default: cozystack-apps
SOURCE_NAME="cozystack-apps"
case "$PWD" in
*"/apps/"*) SOURCE_NAME="cozystack-apps" ;;
*"/extra/"*) SOURCE_NAME="cozystack-extra" ;;
esac
# If file doesn't exist, create a minimal skeleton
OUT="${OUT:-$CRD_DIR/$NAME.yaml}"
if [[ ! -f "$OUT" ]]; then
cat >"$OUT" <<EOF
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: ${NAME}
spec: {}
EOF
fi
# Export vars for yq env()
export RES_NAME="$NAME"
export PREFIX="$NAME-"
if [ "$SOURCE_NAME" == "cozystack-extra" ]; then
export PREFIX=""
fi
export DESCRIPTION="$DESC"
export ICON_B64="$ICON_B64"
export SOURCE_NAME="$SOURCE_NAME"
export SCHEMA_JSON_MIN="$(jq -c . "$SCHEMA_JSON")"
# Generate keysOrder from values.yaml
export KEYS_ORDER="$(
yq -o=json '.' "$VALUES_YAML" | jq -c '
def get_paths_recursive(obj; path):
obj | to_entries | map(
.key as $key |
.value as $value |
if $value | type == "object" then
[path + [$key]] + get_paths_recursive($value; path + [$key])
else
[path + [$key]]
end
) | flatten(1)
;
(
[ ["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata","name"] ]
)
+
(
get_paths_recursive(.; []) # get all paths in order
| map(select(length>0)) # drop root
| map(map(select(type != "number"))) # drop array indices
| map(["spec"] + .) # prepend "spec"
)
'
)"
# Update only necessary fields in-place
# - openAPISchema is loaded from file as a multi-line string (block scalar)
# - labels ensure cozystack.io/ui: "true"
# - prefix = "<name>-"
# - sourceRef derived from directory (apps|extra)
yq -i '
.apiVersion = (.apiVersion // "cozystack.io/v1alpha1") |
.kind = (.kind // "CozystackResourceDefinition") |
.metadata.name = strenv(RES_NAME) |
.spec.application.openAPISchema = strenv(SCHEMA_JSON_MIN) |
(.spec.application.openAPISchema style="literal") |
.spec.release.prefix = (strenv(PREFIX)) |
.spec.release.labels."cozystack.io/ui" = "true" |
.spec.release.chart.name = strenv(RES_NAME) |
.spec.release.chart.sourceRef.kind = "HelmRepository" |
.spec.release.chart.sourceRef.name = strenv(SOURCE_NAME) |
.spec.release.chart.sourceRef.namespace = "cozy-public" |
.spec.dashboard.description = strenv(DESCRIPTION) |
.spec.dashboard.icon = strenv(ICON_B64) |
.spec.dashboard.keysOrder = env(KEYS_ORDER)
' "$OUT"
echo "Updated $OUT"

View File

@@ -9,6 +9,7 @@ import (
"sync"
"time"
"github.com/cozystack/cozystack/internal/controller/dashboard"
"github.com/cozystack/cozystack/internal/shared/crdmem"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
@@ -50,6 +51,25 @@ func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, r
r.mem.Upsert(crd)
}
mgr := dashboard.NewManager(
r.Client,
r.Scheme,
dashboard.WithCRDListFunc(func(c context.Context) ([]cozyv1alpha1.CozystackResourceDefinition, error) {
if r.mem != nil {
return r.mem.ListFromCacheOrAPI(c, r.Client)
}
var list cozyv1alpha1.CozystackResourceDefinitionList
if err := r.Client.List(c, &list); err != nil {
return nil, err
}
return list.Items, nil
}),
)
if res, derr := mgr.EnsureForCRD(ctx, crd); derr != nil || res.Requeue || res.RequeueAfter > 0 {
return res, derr
}
r.mu.Lock()
r.lastEvent = time.Now()
r.mu.Unlock()
@@ -102,17 +122,23 @@ type crdHashView struct {
Spec cozyv1alpha1.CozystackResourceDefinitionSpec `json:"spec"`
}
func (r *CozystackResourceDefinitionReconciler) computeConfigHash() (string, error) {
if r.mem == nil {
return "", nil
func (r *CozystackResourceDefinitionReconciler) computeConfigHash(ctx context.Context) (string, error) {
var items []cozyv1alpha1.CozystackResourceDefinition
if r.mem != nil {
list, err := r.mem.ListFromCacheOrAPI(ctx, r.Client)
if err != nil {
return "", err
}
items = list
}
snapshot := r.mem.Snapshot()
sort.Slice(snapshot, func(i, j int) bool { return snapshot[i].Name < snapshot[j].Name })
views := make([]crdHashView, 0, len(snapshot))
for i := range snapshot {
sort.Slice(items, func(i, j int) bool { return items[i].Name < items[j].Name })
views := make([]crdHashView, 0, len(items))
for i := range items {
views = append(views, crdHashView{
Name: snapshot[i].Name,
Spec: snapshot[i].Spec,
Name: items[i].Name,
Spec: items[i].Spec,
})
}
b, err := json.Marshal(views)
@@ -143,7 +169,7 @@ func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Con
return ctrl.Result{}, nil
}
newHash, err := r.computeConfigHash()
newHash, err := r.computeConfigHash(ctx)
if err != nil {
return ctrl.Result{}, err
}

View File

@@ -0,0 +1,298 @@
package dashboard
import (
"context"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureCustomColumnsOverride creates or updates a CustomColumnsOverride that
// renders a header row with a colored badge and resource name link, plus a few
// useful columns (Ready, Created, Version).
//
// Naming convention mirrors your example:
//
// metadata.name: stock-namespace-<group>.<version>.<plural>
// spec.id: stock-namespace-/<group>/<version>/<plural>
func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (controllerutil.OperationResult, error) {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
// Details page segment uses lowercase kind, mirroring your example
detailsSegment := strings.ToLower(kind) + "-details"
name := fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
id := fmt.Sprintf("stock-namespace-/%s/%s/%s", g, v, plural)
// Badge content & color derived from kind
badgeText := initialsFromKind(kind) // e.g., "VirtualMachine" -> "VM", "Bucket" -> "B"
badgeColor := hexColorForKind(kind) // deterministic, dark enough for white text
obj := &dashv1alpha1.CustomColumnsOverride{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: "dashboard.cozystack.io",
Version: "v1alpha1",
Kind: "CustomColumnsOverride",
})
obj.SetName(name)
href := fmt.Sprintf("/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/%s/{reqsJsonPath[0]['.metadata.name']['-']}", detailsSegment)
if g == "apps.cozystack.io" && kind == "Tenant" && plural == "tenants" {
href = "/openapi-ui/{2}/{reqsJsonPath[0]['.status.namespace']['-']}/api-table/core.cozystack.io/v1alpha1/tenantmodules"
}
desired := map[string]any{
"spec": map[string]any{
"id": id,
"additionalPrinterColumns": []any{
map[string]any{
"name": "Name",
"type": "factory",
"jsonPath": ".metadata.name",
"customProps": map[string]any{
"disableEventBubbling": true,
"items": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "header-row",
"align": "center",
"gap": 6,
},
"children": []any{
map[string]any{
"type": "antdText",
"data": map[string]any{
"id": "header-badge",
"text": badgeText,
"title": strings.ToLower(kind), // optional tooltip
"style": map[string]any{
"backgroundColor": badgeColor,
"borderRadius": "20px",
"color": "#fff",
"display": "inline-block",
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": "15px",
"fontWeight": 400,
"lineHeight": "24px",
"minWidth": 24,
"padding": "0 9px",
"textAlign": "center",
"whiteSpace": "nowrap",
},
},
},
map[string]any{
"type": "antdLink",
"data": map[string]any{
"id": "name-link",
"text": "{reqsJsonPath[0]['.metadata.name']['-']}",
"href": href,
},
},
},
},
},
},
},
map[string]any{
"name": "Ready",
"type": "Boolean",
"jsonPath": `.status.conditions[?(@.type=="Ready")].status`,
},
map[string]any{
"name": "Created",
"type": "factory",
"jsonPath": ".metadata.creationTimestamp",
"customProps": map[string]any{
"disableEventBubbling": true,
"items": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "time-block",
"align": "center",
"gap": 6,
},
"children": []any{
map[string]any{
"type": "antdText",
"data": map[string]any{
"id": "time-icon",
"text": "🌐",
},
},
map[string]any{
"type": "parsedText",
"data": map[string]any{
"id": "time-value",
"text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}",
"formatter": "timestamp",
},
},
},
},
},
},
},
map[string]any{
"name": "Version",
"type": "string",
"jsonPath": ".status.version",
},
},
},
}
// CreateOrUpdate using typed resource
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
b, err := json.Marshal(desired["spec"])
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
return nil
})
// Return OperationResultCreated/Updated is not available here with unstructured; we can mimic Updated when no error.
return controllerutil.OperationResultNone, err
}
// --- helpers ---
// pickGVK tries to read group/version/kind from the CRD. We prefer the "application" section,
// falling back to other likely fields if your schema differs.
func pickGVK(crd *cozyv1alpha1.CozystackResourceDefinition) (group, version, kind string) {
// Best guess based on your examples:
if crd.Spec.Application.Kind != "" {
kind = crd.Spec.Application.Kind
}
// Reasonable fallbacks if any are empty:
if group == "" {
group = "apps.cozystack.io"
}
if version == "" {
version = "v1alpha1"
}
if kind == "" {
kind = "Resource"
}
return
}
// pickPlural prefers a field on the CRD if you have it; otherwise do a simple lowercase + "s".
func pickPlural(kind string, crd *cozyv1alpha1.CozystackResourceDefinition) string {
// If you have crd.Spec.Application.Plural, prefer it. Example:
if crd.Spec.Application.Plural != "" {
return crd.Spec.Application.Plural
}
// naive pluralization
k := strings.ToLower(kind)
if strings.HasSuffix(k, "s") {
return k
}
return k + "s"
}
// initialsFromKind splits CamelCase and returns the first letters in upper case.
// "VirtualMachine" -> "VM"; "Bucket" -> "B".
func initialsFromKind(kind string) string {
parts := splitCamel(kind)
if len(parts) == 0 {
return strings.ToUpper(kind)
}
var b strings.Builder
for _, p := range parts {
if p == "" {
continue
}
b.WriteString(strings.ToUpper(string(p[0])))
// Limit to 3 chars to keep the badge compact (VM, PVC, etc.)
if b.Len() >= 3 {
break
}
}
return b.String()
}
// hexColorForKind returns a dark, saturated color (hex) derived from a stable hash of the kind.
// We map the hash to an HSL hue; fix S/L for consistent readability with white text.
func hexColorForKind(kind string) string {
// Stable short hash (sha1 → bytes → hue)
sum := sha1.Sum([]byte(kind))
// Use first two bytes for hue [0..359]
hue := int(sum[0])<<8 | int(sum[1])
hue = hue % 360
// Fixed S/L chosen to contrast with white text:
// S = 80%, L = 35% (dark enough so #fff is readable)
r, g, b := hslToRGB(float64(hue), 0.80, 0.35)
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
}
// hslToRGB converts HSL (0..360, 0..1, 0..1) to sRGB (0..255).
func hslToRGB(h float64, s float64, l float64) (uint8, uint8, uint8) {
c := (1 - absFloat(2*l-1)) * s
hp := h / 60.0
x := c * (1 - absFloat(modFloat(hp, 2)-1))
var r1, g1, b1 float64
switch {
case 0 <= hp && hp < 1:
r1, g1, b1 = c, x, 0
case 1 <= hp && hp < 2:
r1, g1, b1 = x, c, 0
case 2 <= hp && hp < 3:
r1, g1, b1 = 0, c, x
case 3 <= hp && hp < 4:
r1, g1, b1 = 0, x, c
case 4 <= hp && hp < 5:
r1, g1, b1 = x, 0, c
default:
r1, g1, b1 = c, 0, x
}
m := l - c/2
r := uint8(clamp01(r1+m) * 255.0)
g := uint8(clamp01(g1+m) * 255.0)
b := uint8(clamp01(b1+m) * 255.0)
return r, g, b
}
func absFloat(v float64) float64 {
if v < 0 {
return -v
}
return v
}
func modFloat(a, b float64) float64 {
return a - b*float64(int(a/b))
}
func clamp01(v float64) float64 {
if v < 0 {
return 0
}
if v > 1 {
return 1
}
return v
}
// optional: tiny helper to expose the compact color hash (useful for debugging)
func shortHashHex(s string) string {
sum := sha1.Sum([]byte(s))
return hex.EncodeToString(sum[:4])
}

View File

@@ -0,0 +1,784 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ---------------- Types used by OpenAPI parsing ----------------
type fieldInfo struct {
JSONPathSpec string // dotted path under .spec (e.g., "systemDisk.image")
Label string // "System Disk / Image" or "systemDisk.image"
Description string
}
// ---------------- Public entry: ensure Factory ------------------
func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
lowerKind := strings.ToLower(kind)
factoryName := fmt.Sprintf("%s-details", lowerKind)
resourceFetch := fmt.Sprintf("/api/clusters/{2}/k8s/apis/%s/%s/namespaces/{3}/%s/{6}", g, v, plural)
flags := factoryFeatureFlags(crd)
var keysOrder [][]string
if crd.Spec.Dashboard != nil {
keysOrder = crd.Spec.Dashboard.KeysOrder
}
tabs := []any{
detailsTab(kind, resourceFetch, crd.Spec.Application.OpenAPISchema, keysOrder),
}
if flags.Workloads {
tabs = append(tabs, workloadsTab(kind))
}
if flags.Ingresses {
tabs = append(tabs, ingressesTab(kind))
}
if flags.Services {
tabs = append(tabs, servicesTab(kind))
}
if flags.Secrets {
tabs = append(tabs, secretsTab(kind))
}
tabs = append(tabs, yamlTab(plural))
badgeText := initialsFromKind(kind)
badgeColor := hexColorForKind(kind)
header := map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "header-row",
"align": "center",
"gap": float64(6),
"style": map[string]any{"marginBottom": float64(24)},
},
"children": []any{
map[string]any{
"type": "antdText",
"data": map[string]any{
"id": "badge-" + lowerKind,
"text": badgeText,
"title": strings.ToLower(plural),
"style": map[string]any{
"backgroundColor": badgeColor,
"borderRadius": "20px",
"color": "#fff",
"display": "inline-block",
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": float64(20),
"fontWeight": float64(400),
"lineHeight": "24px",
"minWidth": float64(24),
"padding": "0 9px",
"textAlign": "center",
"whiteSpace": "nowrap",
},
},
},
map[string]any{
"type": "parsedText",
"data": map[string]any{
"id": lowerKind + "-name",
"text": "{reqsJsonPath[0]['.metadata.name']['-']}",
"style": map[string]any{
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": float64(20),
"lineHeight": "24px",
},
},
},
},
}
spec := map[string]any{
"key": factoryName,
"sidebarTags": []any{fmt.Sprintf("%s-sidebar", lowerKind)},
"withScrollableMainContentCard": true,
"urlsToFetch": []any{resourceFetch},
"data": []any{
header,
map[string]any{
"type": "antdTabs",
"data": map[string]any{
"id": lowerKind + "-tabs",
"defaultActiveKey": "details",
"items": tabs,
},
},
},
}
obj := &dashv1alpha1.Factory{}
obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "dashboard.cozystack.io", Version: "v1alpha1", Kind: "Factory"})
obj.SetName(factoryName)
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
b, err := json.Marshal(spec)
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
return nil
})
return err
}
// ---------------- Tabs builders ----------------
func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[string]any {
paramsBlocks := buildOpenAPIParamsBlocks(schemaJSON, keysOrder)
paramsList := map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "params-list",
"vertical": true,
"gap": float64(24),
},
"children": paramsBlocks,
}
leftColStack := []any{
antdText("details-title", true, kind, map[string]any{
"fontSize": float64(20),
"marginBottom": float64(12),
}),
antdFlexVertical("meta-name-block", 4, []any{
antdText("meta-name-label", true, "Name", nil),
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
}),
antdFlexVertical("meta-namespace-block", 8, []any{
antdText("meta-namespace-label", true, "Namespace", nil),
map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "namespace-row",
"align": "center",
"gap": float64(6),
},
"children": []any{
map[string]any{
"type": "antdText",
"data": map[string]any{
"id": "ns-badge",
"text": "NS",
"style": map[string]any{
"backgroundColor": "#a25792ff",
"borderRadius": "20px",
"color": "#fff",
"display": "inline-block",
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": float64(15),
"fontWeight": float64(400),
"lineHeight": "24px",
"minWidth": float64(24),
"padding": "0 9px",
"textAlign": "center",
"whiteSpace": "nowrap",
},
},
},
antdLink("namespace-link",
"{reqsJsonPath[0]['.metadata.namespace']['-']}",
"/openapi-ui/{2}/factory/namespace-details/{reqsJsonPath[0]['.metadata.namespace']['-']}",
),
},
},
}),
antdFlexVertical("meta-created-block", 4, []any{
antdText("time-label", true, "Created", nil),
antdFlex("time-block", 6, []any{
antdText("time-icon", false, "🌐", nil),
parsedTextWithFormatter("time-value", "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", "timestamp"),
}),
}),
antdFlexVertical("meta-version-block", 4, []any{
antdText("version-label", true, "Version", nil),
parsedText("version-value", "{reqsJsonPath[0]['.status.version']['-']}", nil),
}),
antdFlexVertical("meta-released-block", 4, []any{
antdText("released-label", true, "Released", nil),
parsedText("released-value", "{reqsJsonPath[0]['.status.conditions[?(@.type==\"Released\")].status']['-']}", nil),
}),
antdFlexVertical("meta-ready-block", 4, []any{
antdText("ready-label", true, "Ready", nil),
parsedText("ready-value", "{reqsJsonPath[0]['.status.conditions[?(@.type==\"Ready\")].status']['-']}", nil),
}),
}
rightColStack := []any{
antdText("params-title", true, "Parameters", map[string]any{
"fontSize": float64(20),
"marginBottom": float64(12),
}),
paramsList,
}
return map[string]any{
"key": "details",
"label": "Details",
"children": []any{
contentCard("details-card", map[string]any{"marginBottom": float64(24)}, []any{
map[string]any{
"type": "antdRow",
"data": map[string]any{
"id": "details-grid",
"gutter": []any{float64(48), float64(12)},
},
"children": []any{
map[string]any{
"type": "antdCol",
"data": map[string]any{"id": "col-left", "span": float64(12)},
"children": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{"id": "col-left-stack", "vertical": true, "gap": float64(24)},
"children": leftColStack,
},
},
},
map[string]any{
"type": "antdCol",
"data": map[string]any{"id": "col-right", "span": float64(12)},
"children": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{"id": "col-right-stack", "vertical": true, "gap": float64(24)},
"children": rightColStack,
},
},
},
},
},
spacer("conditions-top-spacer", float64(16)),
antdText("conditions-title", true, "Conditions", map[string]any{"fontSize": float64(20)}),
spacer("conditions-spacer", float64(8)),
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "conditions-table",
"fetchUrl": endpoint,
"clusterNamePartOfUrl": "{2}",
"customizationId": "factory-status-conditions",
"baseprefix": "/openapi-ui",
"withoutControls": true,
"pathToItems": []any{"status", "conditions"},
},
},
}),
},
}
}
func workloadsTab(kind string) map[string]any {
return map[string]any{
"key": "workloads",
"label": "Workloads",
"children": []any{
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "workloads-table",
"fetchUrl": "/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloadmonitors",
"clusterNamePartOfUrl": "{2}",
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1alpha1.cozystack.io.workloadmonitors",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
},
},
},
},
}
}
func servicesTab(kind string) map[string]any {
return map[string]any{
"key": "services",
"label": "Services",
"children": []any{
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "services-table",
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services",
"clusterNamePartOfUrl": "{2}",
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1.services",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
},
},
},
},
}
}
func ingressesTab(kind string) map[string]any {
return map[string]any{
"key": "ingresses",
"label": "Ingresses",
"children": []any{
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "ingresses-table",
"fetchUrl": "/api/clusters/{2}/k8s/apis/networking.k8s.io/v1/namespaces/{3}/ingresses",
"clusterNamePartOfUrl": "{2}",
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-networking.k8s.io.v1.ingresses",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
},
},
},
},
}
}
func secretsTab(kind string) map[string]any {
return map[string]any{
"key": "secrets",
"label": "Secrets",
"children": []any{
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "secrets-table",
"fetchUrl": "/api/clusters/{2}/k8s/apis/core.cozystack.io/v1alpha1/namespaces/{3}/tenantsecretstables",
"clusterNamePartOfUrl": "{2}",
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1alpha1.core.cozystack.io.tenantsecretstables",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
},
},
},
},
}
}
func yamlTab(plural string) map[string]any {
return map[string]any{
"key": "yaml",
"label": "YAML",
"children": []any{
map[string]any{
"type": "YamlEditorSingleton",
"data": map[string]any{
"id": "yaml-editor",
"cluster": "{2}",
"isNameSpaced": true,
"type": "builtin",
"typeName": plural,
"prefillValuesRequestIndex": float64(0),
"substractHeight": float64(400),
},
},
},
}
}
// ---------------- UI helpers (use float64 for numeric fields) ----------------
func contentCard(id string, style map[string]any, children []any) map[string]any {
return map[string]any{
"type": "ContentCard",
"data": map[string]any{
"id": id,
"style": style,
},
"children": children,
}
}
func antdText(id string, strong bool, text string, style map[string]any) map[string]any {
data := map[string]any{
"id": id,
"text": text,
"strong": strong,
}
if style != nil {
data["style"] = style
}
return map[string]any{"type": "antdText", "data": data}
}
func parsedText(id, text string, style map[string]any) map[string]any {
data := map[string]any{
"id": id,
"text": text,
}
if style != nil {
data["style"] = style
}
return map[string]any{"type": "parsedText", "data": data}
}
func parsedTextWithFormatter(id, text, formatter string) map[string]any {
return map[string]any{
"type": "parsedText",
"data": map[string]any{
"id": id,
"text": text,
"formatter": formatter,
},
}
}
func spacer(id string, space float64) map[string]any {
return map[string]any{
"type": "Spacer",
"data": map[string]any{
"id": id,
"$space": space,
},
}
}
func antdFlex(id string, gap float64, children []any) map[string]any {
return map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": id,
"align": "center",
"gap": gap,
},
"children": children,
}
}
func antdFlexVertical(id string, gap float64, children []any) map[string]any {
return map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": id,
"vertical": true,
"gap": gap,
},
"children": children,
}
}
func antdRow(id string, gutter []any, children []any) map[string]any {
return map[string]any{
"type": "antdRow",
"data": map[string]any{
"id": id,
"gutter": gutter,
},
"children": children,
}
}
func antdCol(id string, span float64, children []any) map[string]any {
return map[string]any{
"type": "antdCol",
"data": map[string]any{
"id": id,
"span": span,
},
"children": children,
}
}
func antdColWithStyle(id string, style map[string]any, children []any) map[string]any {
return map[string]any{
"type": "antdCol",
"data": map[string]any{
"id": id,
"style": style,
},
"children": children,
}
}
func antdLink(id, text, href string) map[string]any {
return map[string]any{
"type": "antdLink",
"data": map[string]any{
"id": id,
"text": text,
"href": href,
},
}
}
// ---------------- OpenAPI → Right column ----------------
func buildOpenAPIParamsBlocks(schemaJSON string, keysOrder [][]string) []any {
var blocks []any
fields := collectOpenAPILeafFields(schemaJSON, 2, 20)
// Sort fields according to keysOrder if provided
if len(keysOrder) > 0 {
fields = sortFieldsByKeysOrder(fields, keysOrder)
}
for idx, f := range fields {
id := fmt.Sprintf("param-%d", idx)
blocks = append(blocks,
antdFlexVertical(id, 4, []any{
antdText(id+"-label", true, f.Label, nil),
parsedText(id+"-value", fmt.Sprintf("{reqsJsonPath[0]['.spec.%s']['-']}", f.JSONPathSpec), nil),
}),
)
}
if len(fields) == 0 {
blocks = append(blocks,
antdText("params-empty", false, "No scalar parameters detected in schema (see YAML tab for full spec).", map[string]any{"opacity": float64(0.7)}),
)
}
return blocks
}
// sortFieldsByKeysOrder sorts fields according to the provided keysOrder
func sortFieldsByKeysOrder(fields []fieldInfo, keysOrder [][]string) []fieldInfo {
// Create a map for quick lookup of field positions
orderMap := make(map[string]int)
for i, path := range keysOrder {
// Convert path to dot notation (e.g., ["spec", "systemDisk", "image"] -> "systemDisk.image")
if len(path) > 1 && path[0] == "spec" {
dotPath := strings.Join(path[1:], ".")
orderMap[dotPath] = i
}
}
// Sort fields based on their position in keysOrder
sort.Slice(fields, func(i, j int) bool {
posI, existsI := orderMap[fields[i].JSONPathSpec]
posJ, existsJ := orderMap[fields[j].JSONPathSpec]
// If both exist in orderMap, sort by position
if existsI && existsJ {
return posI < posJ
}
// If only one exists, prioritize the one that exists
if existsI {
return true
}
if existsJ {
return false
}
// If neither exists, maintain original order (stable sort)
return i < j
})
return fields
}
func collectOpenAPILeafFields(schemaJSON string, maxDepth, maxFields int) []fieldInfo {
type node = map[string]any
if strings.TrimSpace(schemaJSON) == "" {
return nil
}
var root any
if err := json.Unmarshal([]byte(schemaJSON), &root); err != nil {
// invalid JSON — skip
return nil
}
props := map[string]any{}
if m, ok := root.(node); ok {
if p, ok := m["properties"].(node); ok {
props = p
}
}
if len(props) == 0 {
return nil
}
var out []fieldInfo
var visit func(prefix []string, n node, depth int)
addField := func(path []string, schema node) {
// Skip excluded paths (backup/bootstrap/password)
if shouldExcludeParamPath(path) {
return
}
// build label "Foo Bar / Baz"
label := humanizePath(path)
desc := getString(schema, "description")
out = append(out, fieldInfo{
JSONPathSpec: strings.Join(path, "."),
Label: label,
Description: desc,
})
}
visit = func(prefix []string, n node, depth int) {
if len(out) >= maxFields {
return
}
// Scalar?
if isScalarType(n) || isIntOrString(n) || hasEnum(n) {
addField(prefix, n)
return
}
// Object with properties
if props, ok := n["properties"].(node); ok {
if depth >= maxDepth {
// too deep — stop
return
}
// deterministic ordering
keys := make([]string, 0, len(props))
for k := range props {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
child, _ := props[k].(node)
visit(append(prefix, k), child, depth+1)
if len(out) >= maxFields {
return
}
}
return
}
// Arrays: try to render item if its scalar and depth limit allows
if n["type"] == "array" {
if items, ok := n["items"].(node); ok && (isScalarType(items) || isIntOrString(items) || hasEnum(items)) {
addField(prefix, items)
}
return
}
// Otherwise skip (unknown/complex)
}
// top-level: iterate properties
keys := make([]string, 0, len(props))
for k := range props {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
if child, ok := props[k].(node); ok {
visit([]string{k}, child, 1)
if len(out) >= maxFields {
break
}
}
}
return out
}
// --- helpers for schema inspection ---
func isScalarType(n map[string]any) bool {
switch getString(n, "type") {
case "string", "integer", "number", "boolean":
return true
default:
return false
}
}
func isIntOrString(n map[string]any) bool {
// Kubernetes extension: x-kubernetes-int-or-string: true
if v, ok := n["x-kubernetes-int-or-string"]; ok {
if b, ok := v.(bool); ok && b {
return true
}
}
// anyOf: integer|string
if anyOf, ok := n["anyOf"].([]any); ok {
hasInt := false
hasStr := false
for _, it := range anyOf {
if m, ok := it.(map[string]any); ok {
switch getString(m, "type") {
case "integer":
hasInt = true
case "string":
hasStr = true
}
}
}
return hasInt && hasStr
}
return false
}
func hasEnum(n map[string]any) bool {
_, ok := n["enum"]
return ok
}
func getString(n map[string]any, key string) string {
if v, ok := n[key]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
// shouldExcludeParamPath returns true if any part of the path contains
// backup / bootstrap / password (case-insensitive)
func shouldExcludeParamPath(parts []string) bool {
for _, p := range parts {
lp := strings.ToLower(p)
if strings.Contains(lp, "backup") || strings.Contains(lp, "bootstrap") || strings.Contains(lp, "password") || strings.Contains(lp, "cloudInit") {
return true
}
}
return false
}
func humanizePath(parts []string) string {
// "systemDisk.image" -> "System Disk / Image"
return strings.Join(parts, " / ")
}
// ---------------- Feature flags ----------------
type factoryFlags struct {
Workloads bool
Ingresses bool
Services bool
Secrets bool
}
// factoryFeatureFlags tries several conventional locations so you can evolve the API
// without breaking the controller. Defaults are false (hidden).
func factoryFeatureFlags(crd *cozyv1alpha1.CozystackResourceDefinition) factoryFlags {
var f factoryFlags
f.Workloads = true
f.Ingresses = true
f.Services = true
f.Secrets = true
return f
}

View File

@@ -0,0 +1,343 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"reflect"
"regexp"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// AddToScheme exposes dashboard types registration for controller setup.
func AddToScheme(s *runtime.Scheme) error {
return dashv1alpha1.AddToScheme(s)
}
// Manager owns logic for creating/updating dashboard resources derived from CRDs.
// Its easy to extend: add new ensure* methods and wire them into EnsureForCRD.
type Manager struct {
client client.Client
scheme *runtime.Scheme
crdListFn func(context.Context) ([]cozyv1alpha1.CozystackResourceDefinition, error)
}
// Option pattern so callers can inject a custom lister.
type Option func(*Manager)
// WithCRDListFunc overrides how Manager lists all CozystackResourceDefinitions.
func WithCRDListFunc(fn func(context.Context) ([]cozyv1alpha1.CozystackResourceDefinition, error)) Option {
return func(m *Manager) { m.crdListFn = fn }
}
// NewManager constructs a dashboard Manager.
func NewManager(c client.Client, scheme *runtime.Scheme, opts ...Option) *Manager {
m := &Manager{client: c, scheme: scheme}
for _, o := range opts {
o(m)
}
return m
}
// EnsureForCRD is the single entry-point used by the controller.
// Add more ensure* calls here as you implement support for other resources:
//
// - ensureBreadcrumb (implemented)
// - ensureCustomColumnsOverride (implemented)
// - ensureCustomFormsOverride (implemented)
// - ensureCustomFormsPrefill (implemented)
// - ensureFactory
// - ensureMarketplacePanel (implemented)
// - ensureSidebar (implemented)
// - ensureTableUriMapping (implemented)
func (m *Manager) EnsureForCRD(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
// MarketplacePanel
if res, err := m.ensureMarketplacePanel(ctx, crd); err != nil || res.Requeue || res.RequeueAfter > 0 {
return res, err
}
// CustomFormsPrefill
if res, err := m.ensureCustomFormsPrefill(ctx, crd); err != nil || res.Requeue || res.RequeueAfter > 0 {
return res, err
}
// CustomColumnsOverride
if _, err := m.ensureCustomColumnsOverride(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureTableUriMapping(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureBreadcrumb(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureCustomFormsOverride(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureSidebar(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureFactory(ctx, crd); err != nil {
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
// ----------------------- MarketplacePanel -----------------------
func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
logger := log.FromContext(ctx)
mp := &dashv1alpha1.MarketplacePanel{}
mp.Name = crd.Name // cluster-scoped resource, name mirrors CRD name
// If dashboard is not set, delete the panel if it exists.
if crd.Spec.Dashboard == nil {
err := m.client.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, err
}
if err := m.client.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
}
logger.Info("Deleted MarketplacePanel because dashboard is not set", "name", mp.Name)
return reconcile.Result{}, nil
}
// Skip resources with non-empty spec.dashboard.name
if strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
err := m.client.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, err
}
if err := m.client.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
}
logger.Info("Deleted MarketplacePanel because spec.dashboard.name is set", "name", mp.Name)
return reconcile.Result{}, nil
}
// Build desired spec from CRD fields
d := crd.Spec.Dashboard
app := crd.Spec.Application
displayName := d.Singular
if displayName == "" {
displayName = app.Kind
}
tags := make([]any, len(d.Tags))
for i, t := range d.Tags {
tags[i] = t
}
specMap := map[string]any{
"description": d.Description,
"name": displayName,
"type": "nonCrd",
"apiGroup": "apps.cozystack.io",
"apiVersion": "v1alpha1",
"typeName": app.Plural, // e.g., "buckets"
"disabled": false,
"hidden": false,
"tags": tags,
"icon": d.Icon,
}
specBytes, err := json.Marshal(specMap)
if err != nil {
return reconcile.Result{}, err
}
mutate := func() error {
if err := controllerutil.SetOwnerReference(crd, mp, m.scheme); err != nil {
return err
}
// Inline JSON payload (the ArbitrarySpec type inlines apiextv1.JSON)
mp.Spec = dashv1alpha1.ArbitrarySpec{
JSON: apiextv1.JSON{Raw: specBytes},
}
return nil
}
op, err := controllerutil.CreateOrUpdate(ctx, m.client, mp, mutate)
if err != nil {
return reconcile.Result{}, err
}
switch op {
case controllerutil.OperationResultCreated:
logger.Info("Created MarketplacePanel", "name", mp.Name)
case controllerutil.OperationResultUpdated:
logger.Info("Updated MarketplacePanel", "name", mp.Name)
}
return reconcile.Result{}, nil
}
// ----------------------- Helpers (OpenAPI → values) -----------------------
// defaultOrZero returns the schema default if present; otherwise a reasonable zero value.
func defaultOrZero(sub map[string]interface{}) interface{} {
if v, ok := sub["default"]; ok {
return v
}
typ, _ := sub["type"].(string)
switch typ {
case "string":
return ""
case "boolean":
return false
case "array":
return []interface{}{}
case "integer", "number":
return 0
case "object":
return map[string]interface{}{}
default:
return nil
}
}
// toIfaceSlice converts []string -> []interface{}.
func toIfaceSlice(ss []string) []interface{} {
out := make([]interface{}, len(ss))
for i, s := range ss {
out[i] = s
}
return out
}
// buildPrefillValues converts an OpenAPI schema (JSON string) into a []interface{} "values" list
// suitable for CustomFormsPrefill.spec.values.
// Rules:
// - For top-level primitive/array fields: emit an entry, using default if present, otherwise zero.
// - For top-level objects: recursively process nested objects and emit entries for all default values
// found at any nesting level.
func buildPrefillValues(openAPISchema string) ([]interface{}, error) {
var root map[string]interface{}
if err := json.Unmarshal([]byte(openAPISchema), &root); err != nil {
return nil, fmt.Errorf("cannot parse openAPISchema: %w", err)
}
props, _ := root["properties"].(map[string]interface{})
if props == nil {
return []interface{}{}, nil
}
var values []interface{}
processSchemaProperties(props, []string{"spec"}, &values)
return values, nil
}
// processSchemaProperties recursively processes OpenAPI schema properties and extracts default values
func processSchemaProperties(props map[string]interface{}, path []string, values *[]interface{}) {
for pname, raw := range props {
sub, _ := raw.(map[string]interface{})
if sub == nil {
continue
}
typ, _ := sub["type"].(string)
currentPath := append(path, pname)
switch typ {
case "object":
// Check if this object has a default value
if objDefault, ok := sub["default"].(map[string]interface{}); ok {
// Process the default object recursively
processDefaultObject(objDefault, currentPath, values)
}
// Also process child properties for their individual defaults
if childProps, ok := sub["properties"].(map[string]interface{}); ok {
processSchemaProperties(childProps, currentPath, values)
}
default:
// For primitive types, use default if present, otherwise zero value
val := defaultOrZero(sub)
if val != nil {
entry := map[string]interface{}{
"path": toIfaceSlice(currentPath),
"value": val,
}
*values = append(*values, entry)
}
}
}
}
// processDefaultObject recursively processes a default object and creates entries for all nested values
func processDefaultObject(obj map[string]interface{}, path []string, values *[]interface{}) {
for key, value := range obj {
currentPath := append(path, key)
// If the value is a map, process it recursively
if nestedObj, ok := value.(map[string]interface{}); ok {
processDefaultObject(nestedObj, currentPath, values)
} else {
// For primitive values, create an entry
entry := map[string]interface{}{
"path": toIfaceSlice(currentPath),
"value": value,
}
*values = append(*values, entry)
}
}
}
// normalizeJSON makes maps/slices JSON-safe for k8s Unstructured:
// - converts all int/int32/... to float64
// - leaves strings, bools, nil as-is
func normalizeJSON(v any) any {
switch t := v.(type) {
case map[string]any:
out := make(map[string]any, len(t))
for k, val := range t {
out[k] = normalizeJSON(val)
}
return out
case []any:
out := make([]any, len(t))
for i := range t {
out[i] = normalizeJSON(t[i])
}
return out
case int:
return float64(t)
case int8:
return float64(t)
case int16:
return float64(t)
case int32:
return float64(t)
case int64:
return float64(t)
case uint, uint8, uint16, uint32, uint64:
return float64(reflect.ValueOf(t).Convert(reflect.TypeOf(uint64(0))).Uint())
case float32:
return float64(t)
default:
return v
}
}
var camelSplitter = regexp.MustCompile(`(?m)([A-Z]+[a-z0-9]*|[a-z0-9]+)`)
func splitCamel(s string) []string {
return camelSplitter.FindAllString(s, -1)
}

View File

@@ -0,0 +1,310 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureSidebar creates/updates multiple Sidebar resources that share the same menu:
// - The "details" sidebar tied to the current kind (stock-project-factory-<kind>-details)
// - The stock-instance sidebars: api-form, api-table, builtin-form, builtin-table
// - The stock-project sidebars: api-form, api-table, builtin-form, builtin-table, crd-form, crd-table
//
// Menu rules:
// - The first section is "Marketplace" with two hardcoded entries:
// - Marketplace (/openapi-ui/{clusterName}/{namespace}/factory/marketplace)
// - Tenant Info (/openapi-ui/{clusterName}/{namespace}/factory/info-details/info)
// - All other sections are built from CRDs where spec.dashboard != nil.
// - Categories are ordered strictly as:
// Marketplace, IaaS, PaaS, NaaS, <others A→Z>, Resources, Administration
// - Items within each category: sort by Weight (desc), then Label (A→Z).
func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
// Build the full menu once.
// 1) Fetch all CRDs
var all []cozyv1alpha1.CozystackResourceDefinition
if m.crdListFn != nil {
s, err := m.crdListFn(ctx)
if err != nil {
return err
}
all = s
} else {
var crdList cozyv1alpha1.CozystackResourceDefinitionList
if err := m.client.List(ctx, &crdList, &client.ListOptions{}); err != nil {
return err
}
all = crdList.Items
}
// 2) Build category -> []item map (only for CRDs with spec.dashboard != nil)
type item struct {
Key string
Label string
Link string
Weight int
}
categories := map[string][]item{} // category label -> children
keysAndTags := map[string]any{} // plural -> []string{ "<lower(kind)>-sidebar" }
for i := range all {
def := &all[i]
// Include ONLY when spec.dashboard != nil
if def.Spec.Dashboard == nil {
continue
}
// Skip resources with non-empty spec.dashboard.name
if strings.TrimSpace(def.Spec.Dashboard.Name) != "" {
continue
}
g, v, kind := pickGVK(def)
plural := pickPlural(kind, def)
cat := safeCategory(def) // falls back to "Resources" if empty
// Label: prefer dashboard.Plural if provided
label := titleFromKindPlural(kind, plural)
if def.Spec.Dashboard.Plural != "" {
label = def.Spec.Dashboard.Plural
}
// Weight (default 0)
weight := def.Spec.Dashboard.Weight
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/%s/%s/%s", g, v, plural)
categories[cat] = append(categories[cat], item{
Key: plural,
Label: label,
Link: link,
Weight: weight,
})
// keysAndTags: plural -> [ "<lower(kind)>-sidebar" ]
keysAndTags[plural] = []any{fmt.Sprintf("%s-sidebar", strings.ToLower(kind))}
}
// 3) Sort items within each category by Weight (desc), then Label (A→Z)
for cat := range categories {
sort.Slice(categories[cat], func(i, j int) bool {
if categories[cat][i].Weight != categories[cat][j].Weight {
return categories[cat][i].Weight < categories[cat][j].Weight // lower weight first
}
return strings.ToLower(categories[cat][i].Label) < strings.ToLower(categories[cat][j].Label)
})
}
// 4) Order categories strictly:
// Marketplace (hardcoded), IaaS, PaaS, NaaS, <others A→Z>, Resources, Administration
orderedCats := orderCategoryLabels(categories)
// 5) Build menuItems (hardcode "Marketplace"; then dynamic categories; then hardcode "Administration")
menuItems := []any{
map[string]any{
"key": "marketplace",
"label": "Marketplace",
"children": []any{
map[string]any{
"key": "marketplace",
"label": "Marketplace",
"link": "/openapi-ui/{clusterName}/{namespace}/factory/marketplace",
},
},
},
}
for _, cat := range orderedCats {
// Skip "Marketplace" and "Administration" here since they're hardcoded
if strings.EqualFold(cat, "Marketplace") || strings.EqualFold(cat, "Administration") {
continue
}
children := []any{}
for _, it := range categories[cat] {
children = append(children, map[string]any{
"key": it.Key,
"label": it.Label,
"link": it.Link,
})
}
if len(children) > 0 {
menuItems = append(menuItems, map[string]any{
"key": slugify(cat),
"label": cat,
"children": children,
})
}
}
// Add hardcoded Administration section
menuItems = append(menuItems, map[string]any{
"key": "administration",
"label": "Administration",
"children": []any{
map[string]any{
"key": "info",
"label": "Info",
"link": "/openapi-ui/{clusterName}/{namespace}/factory/info-details/info",
},
map[string]any{
"key": "modules",
"label": "Modules",
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules",
},
map[string]any{
"key": "tenants",
"label": "Tenants",
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/apps.cozystack.io/v1alpha1/tenants",
},
},
})
// 6) Prepare the list of Sidebar IDs to upsert with the SAME content
_, _, thisKind := pickGVK(crd)
lowerThisKind := strings.ToLower(thisKind)
detailsID := fmt.Sprintf("stock-project-factory-%s-details", lowerThisKind)
targetIDs := []string{
// original details sidebar
detailsID,
// stock-instance sidebars
"stock-instance-api-form",
"stock-instance-api-table",
"stock-instance-builtin-form",
"stock-instance-builtin-table",
// stock-project sidebars
"stock-project-factory-marketplace",
"stock-project-factory-workloadmonitor-details",
"stock-project-api-form",
"stock-project-api-table",
"stock-project-builtin-form",
"stock-project-builtin-table",
"stock-project-crd-form",
"stock-project-crd-table",
}
// 7) Upsert all target sidebars with identical menuItems and keysAndTags
return m.upsertMultipleSidebars(ctx, crd, targetIDs, keysAndTags, menuItems)
}
// upsertMultipleSidebars creates/updates several Sidebar resources with the same menu spec.
func (m *Manager) upsertMultipleSidebars(
ctx context.Context,
crd *cozyv1alpha1.CozystackResourceDefinition,
ids []string,
keysAndTags map[string]any,
menuItems []any,
) error {
for _, id := range ids {
spec := map[string]any{
"id": id,
"keysAndTags": keysAndTags,
"menuItems": menuItems,
}
obj := &dashv1alpha1.Sidebar{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: "dashboard.cozystack.io",
Version: "v1alpha1",
Kind: "Sidebar",
})
obj.SetName(id)
if _, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
b, err := json.Marshal(spec)
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
return nil
}); err != nil {
return err
}
}
return nil
}
// orderCategoryLabels returns category labels ordered strictly as:
//
// Marketplace, IaaS, PaaS, NaaS, <others A→Z>, Resources, Administration.
//
// It only returns labels that exist in `cats` (except "Marketplace" which is hardcoded by caller).
func orderCategoryLabels[T any](cats map[string][]T) []string {
if len(cats) == 0 {
return []string{"Marketplace", "IaaS", "PaaS", "NaaS", "Resources", "Administration"}
}
head := []string{"Marketplace", "IaaS", "PaaS", "NaaS"}
tail := []string{"Resources", "Administration"}
present := make(map[string]struct{}, len(cats))
for k := range cats {
present[k] = struct{}{}
}
var result []string
// Add head anchors (keep "Marketplace" in the order signature for the caller)
for _, h := range head {
result = append(result, h)
delete(present, h)
}
// Collect "others": exclude tail
var others []string
for k := range present {
if k == "Resources" || k == "Administration" {
continue
}
others = append(others, k)
}
sort.Slice(others, func(i, j int) bool { return strings.ToLower(others[i]) < strings.ToLower(others[j]) })
// Append others, then tail (always in fixed order)
result = append(result, others...)
result = append(result, tail...)
return result
}
// safeCategory returns spec.dashboard.category or "Resources" if not set.
func safeCategory(def *cozyv1alpha1.CozystackResourceDefinition) string {
if def == nil || def.Spec.Dashboard == nil {
return "Resources"
}
if def.Spec.Dashboard.Category != "" {
return def.Spec.Dashboard.Category
}
return "Resources"
}
// slugify converts a category label to a key-friendly identifier.
// "User Management" -> "usermanagement", "PaaS" -> "paas".
func slugify(s string) string {
s = strings.TrimSpace(strings.ToLower(s))
out := make([]byte, 0, len(s))
for i := 0; i < len(s); i++ {
c := s[i]
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') {
out = append(out, c)
}
}
return string(out)
}

View File

@@ -0,0 +1,266 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// Ensure the three additional dashboard/frontend resources exist:
// - TableUriMapping (dashboard.cozystack.io/v1alpha1)
// - Breadcrumb (dashboard.cozystack.io/v1alpha1)
// - CustomFormsOverride (dashboard.cozystack.io/v1alpha1)
//
// Call these from Manager.EnsureForCRD() after ensureCustomColumnsOverride.
// --------------------------- TableUriMapping -----------------------------
func (m *Manager) ensureTableUriMapping(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
// Links are fully managed by the CustomColumnsOverride.
return nil
}
// ------------------------------- Breadcrumb -----------------------------
func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
_, _, kind := pickGVK(crd)
lowerKind := strings.ToLower(kind)
detailID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
obj := &dashv1alpha1.Breadcrumb{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: "dashboard.cozystack.io",
Version: "v1alpha1",
Kind: "Breadcrumb",
})
obj.SetName(detailID)
plural := pickPlural(kind, crd)
// Prefer dashboard.Plural for UI label if provided
labelPlural := titleFromKindPlural(kind, plural)
if crd != nil && crd.Spec.Dashboard != nil && crd.Spec.Dashboard.Plural != "" {
labelPlural = crd.Spec.Dashboard.Plural
}
key := plural // e.g., "virtualmachines"
label := labelPlural
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/apps.cozystack.io/v1alpha1/%s", plural)
// If Name is set, change the first breadcrumb item to "Tenant Modules"
// TODO add parameter to this
if crd.Spec.Dashboard.Name != "" {
key = "tenantmodules"
label = "Tenant Modules"
link = "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules"
}
items := []any{
map[string]any{
"key": key,
"label": label,
"link": link,
},
map[string]any{
"key": strings.ToLower(kind), // "etcd"
"label": "{6}", // literal, as in your example
},
}
spec := map[string]any{
"id": detailID,
"breadcrumbItems": items,
}
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
b, err := json.Marshal(spec)
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
return nil
})
return err
}
// --------------------------- CustomFormsOverride ------------------------
func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
name := fmt.Sprintf("%s.%s.%s", g, v, plural)
customizationID := fmt.Sprintf("default-/%s/%s/%s", g, v, plural)
obj := &dashv1alpha1.CustomFormsOverride{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: "dashboard.cozystack.io",
Version: "v1alpha1",
Kind: "CustomFormsOverride",
})
obj.SetName(name)
// Replicates your Helm includes (system metadata + api + status).
hidden := []any{}
hidden = append(hidden, hiddenMetadataSystem()...)
hidden = append(hidden, hiddenMetadataAPI()...)
hidden = append(hidden, hiddenStatus()...)
// If Name is set, hide metadata
if crd.Spec.Dashboard != nil && strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
hidden = append([]interface{}{
[]any{"metadata"},
}, hidden...)
}
sort := make([]any, len(crd.Spec.Dashboard.KeysOrder))
for i, v := range crd.Spec.Dashboard.KeysOrder {
sort[i] = v
}
spec := map[string]any{
"customizationId": customizationID,
"hidden": hidden,
"sort": sort,
"schema": map[string]any{}, // {}
"strategy": "merge",
}
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
b, err := json.Marshal(spec)
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
return nil
})
return err
}
// ----------------------- CustomFormsPrefill -----------------------
func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
logger := log.FromContext(ctx)
app := crd.Spec.Application
group := "apps.cozystack.io"
version := "v1alpha1"
name := fmt.Sprintf("%s.%s.%s", group, version, app.Plural)
customizationID := fmt.Sprintf("default-/%s/%s/%s", group, version, app.Plural)
values, err := buildPrefillValues(app.OpenAPISchema)
if err != nil {
return reconcile.Result{}, err
}
// If Name is set, prefill metadata.name
if crd.Spec.Dashboard != nil && strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
values = append([]interface{}{
map[string]interface{}{
"path": toIfaceSlice([]string{"metadata", "name"}),
"value": crd.Spec.Dashboard.Name,
},
}, values...)
}
cfp := &dashv1alpha1.CustomFormsPrefill{}
cfp.Name = name // cluster-scoped
specMap := map[string]any{
"customizationId": customizationID,
"values": values,
}
specBytes, err := json.Marshal(specMap)
if err != nil {
return reconcile.Result{}, err
}
mutate := func() error {
if err := controllerutil.SetOwnerReference(crd, cfp, m.scheme); err != nil {
return err
}
cfp.Spec = dashv1alpha1.ArbitrarySpec{
JSON: apiextv1.JSON{Raw: specBytes},
}
return nil
}
op, err := controllerutil.CreateOrUpdate(ctx, m.client, cfp, mutate)
if err != nil {
return reconcile.Result{}, err
}
switch op {
case controllerutil.OperationResultCreated:
logger.Info("Created CustomFormsPrefill", "name", cfp.Name)
case controllerutil.OperationResultUpdated:
logger.Info("Updated CustomFormsPrefill", "name", cfp.Name)
}
return reconcile.Result{}, nil
}
// ------------------------------ Helpers ---------------------------------
// titleFromKindPlural returns a presentable plural label, e.g.:
// kind="VirtualMachine", plural="virtualmachines" => "VirtualMachines"
func titleFromKindPlural(kind, plural string) string {
label := kind
if !strings.HasSuffix(strings.ToLower(plural), "s") || !strings.HasSuffix(strings.ToLower(plural), "S") {
label += "s"
} else {
label += "s"
}
return label
}
// The hidden lists below mirror the Helm templates you shared.
// Each entry is a path as nested string array, e.g. ["metadata","creationTimestamp"].
func hiddenMetadataSystem() []any {
return []any{
[]any{"metadata", "annotations"},
[]any{"metadata", "labels"},
[]any{"metadata", "namespace"},
[]any{"metadata", "creationTimestamp"},
[]any{"metadata", "deletionGracePeriodSeconds"},
[]any{"metadata", "deletionTimestamp"},
[]any{"metadata", "finalizers"},
[]any{"metadata", "generateName"},
[]any{"metadata", "generation"},
[]any{"metadata", "managedFields"},
[]any{"metadata", "ownerReferences"},
[]any{"metadata", "resourceVersion"},
[]any{"metadata", "selfLink"},
[]any{"metadata", "uid"},
}
}
func hiddenMetadataAPI() []any {
return []any{
[]any{"kind"},
[]any{"apiVersion"},
[]any{"appVersion"},
}
}
func hiddenStatus() []any {
return []any{
[]any{"status"},
}
}

View File

@@ -25,7 +25,10 @@ type runtimeConfig struct {
func (l *LineageControllerWebhook) initConfig() {
l.initOnce.Do(func() {
if l.config.Load() == nil {
l.config.Store(&runtimeConfig{chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition)})
l.config.Store(&runtimeConfig{
chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition),
appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
})
}
})
}

View File

@@ -5,11 +5,10 @@ import (
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
)
// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=list;watch
// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=list;watch;get
func (c *LineageControllerWebhook) SetupWithManagerAsController(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
@@ -20,7 +19,7 @@ func (c *LineageControllerWebhook) SetupWithManagerAsController(mgr ctrl.Manager
func (c *LineageControllerWebhook) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
l := log.FromContext(ctx)
crds := &cozyv1alpha1.CozystackResourceDefinitionList{}
if err := c.List(ctx, crds, &client.ListOptions{Namespace: "cozy-system"}); err != nil {
if err := c.List(ctx, crds); err != nil {
l.Error(err, "failed reading CozystackResourceDefinitions")
return ctrl.Result{}, err
}

View File

@@ -2,4 +2,5 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
yq -o json -i '.properties = {}' values.schema.json
yq -o json -i '.properties = {}' values.schema.json
../../../hack/update-crd.sh

View File

@@ -5,6 +5,7 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
image:
docker buildx build images/clickhouse-backup \

View File

@@ -2,6 +2,7 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
update:
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/FerretDB/FerretDB | awk -F'[/^]' '{sub("^v", "", $$3)} END{print $$3}') && \

View File

@@ -18,6 +18,7 @@ image-nginx:
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
update:
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/chrislim2888/IP2Location-C-Library | awk -F'[/^]' 'END{print $$3}') && \

View File

@@ -3,3 +3,4 @@ PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"]
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -7,6 +7,7 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
yq -o=json -i '.properties.version.enum = (load("files/versions.yaml") | keys)' values.schema.json
../../../hack/update-crd.sh
image: image-ubuntu-container-disk image-kubevirt-cloud-provider image-kubevirt-csi-driver image-cluster-autoscaler

View File

@@ -5,6 +5,7 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
image:
docker buildx build images/mariadb-backup \

View File

@@ -2,3 +2,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -2,3 +2,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -20,6 +20,8 @@ apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-credentials
labels:
internal.cozystack.io/tenantsecret: "true"
stringData:
{{- range $user, $u := .Values.users }}
{{ quote $user }}: {{ quote (index $passwords $user) }}

View File

@@ -2,3 +2,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -2,3 +2,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -2,3 +2,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -2,3 +2,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -29,6 +29,13 @@ rules:
resources:
- workloadmonitors
verbs: ["get", "list", "watch"]
- apiGroups:
- core.cozystack.io
resources:
- tenantmodules
- tenantsecrets
- tenantsecretstables
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
@@ -178,6 +185,13 @@ rules:
resources:
- workloadmonitors
verbs: ["get", "list", "watch"]
- apiGroups:
- core.cozystack.io
resources:
- tenantmodules
- tenantsecrets
- tenantsecretstables
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -269,6 +283,13 @@ rules:
resources:
- workloadmonitors
verbs: ["get", "list", "watch"]
- apiGroups:
- core.cozystack.io
resources:
- tenantmodules
- tenantsecrets
- tenantsecretstables
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -336,6 +357,13 @@ rules:
resources:
- workloadmonitors
verbs: ["get", "list", "watch"]
- apiGroups:
- core.cozystack.io
resources:
- tenantmodules
- tenantsecrets
- tenantsecretstables
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1

View File

@@ -7,3 +7,4 @@ generate:
# && yq -i -o json ".properties.instanceType.enum = $${INSTANCE_TYPES}" values.schema.json
PREFERENCES=$$(yq e '.metadata.name' -o=json -r ../../system/kubevirt-instancetypes/templates/preferences.yaml | yq 'split(" ") | . + [""]' -o json) \
&& yq -i -o json ".properties.instanceProfile.enum = $${PREFERENCES}" values.schema.json
../../../hack/update-crd.sh

View File

@@ -11,10 +11,12 @@ stringData:
{{- end }}
{{- if or .Values.cloudInit .Values.sshKeys }}
---
apiVersion: v1
kind: Secret
apiVersion: core.cozystack.io/v1alpha1
kind: TenantSecret
metadata:
name: {{ include "virtual-machine.fullname" . }}-cloud-init
labels:
apps.cozystack.io/virtual-machine: {{ .Release.Name }}
stringData:
userdata: |
{{- if .Values.cloudInit }}

View File

@@ -2,3 +2,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -6,3 +6,4 @@ generate:
# && yq -i -o json ".properties.instanceType.enum = $${INSTANCE_TYPES}" values.schema.json
PREFERENCES=$$(yq e '.metadata.name' -o=json -r ../../system/kubevirt-instancetypes/templates/preferences.yaml | yq 'split(" ") | . + [""]' -o json) \
&& yq -i -o json ".properties.instanceProfile.enum = $${PREFERENCES}" values.schema.json
../../../hack/update-crd.sh

View File

@@ -2,3 +2,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -92,7 +92,7 @@ releases:
releaseName: cozystack-api
chart: cozy-cozystack-api
namespace: cozy-system
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,cozystack-controller]
- name: cozystack-controller
releaseName: cozystack-controller
@@ -297,6 +297,24 @@ releases:
- keycloak-configure
{{- end }}
- name: dashboard-config
releaseName: dashboard-config
chart: cozy-dashboard-config
namespace: cozy-dashboard
values:
{{- $dashboardKCconfig := lookup "v1" "ConfigMap" "cozy-dashboard" "kubeapps-auth-config" }}
{{- $dashboardKCValues := dig "data" "values.yaml" "" $dashboardKCconfig | fromYaml }}
{{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }}
dependsOn:
- cilium
- kubeovn
- dashboard
- cozystack-api
{{- if eq $oidcEnabled "true" }}
- keycloak-configure
{{- end }}
- name: kamaji
releaseName: kamaji
chart: cozy-kamaji

View File

@@ -39,7 +39,7 @@ releases:
releaseName: cozystack-api
chart: cozy-cozystack-api
namespace: cozy-system
dependsOn: []
dependsOn: [cozystack-controller]
- name: cozystack-controller
releaseName: cozystack-controller
@@ -177,11 +177,27 @@ releases:
{{- $dashboardKCValues := dig "data" "values.yaml" (dict) $dashboardKCconfig }}
{{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }}
{{- if eq $oidcEnabled "true" }}
dependsOn: [keycloak-configure]
dependsOn: [keycloak-configure,cozystack-api]
{{- else }}
dependsOn: []
{{- end }}
- name: dashboard-config
releaseName: dashboard-config
chart: cozy-dashboard-config
namespace: cozy-dashboard
values:
{{- $dashboardKCconfig := lookup "v1" "ConfigMap" "cozy-dashboard" "kubeapps-auth-config" }}
{{- $dashboardKCValues := dig "data" "values.yaml" "" $dashboardKCconfig | fromYaml }}
{{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }}
dependsOn:
- cilium
- kubeovn
- dashboard
{{- if eq $oidcEnabled "true" }}
- keycloak-configure
{{- end }}
{{- if $oidcEnabled }}
- name: keycloak
releaseName: keycloak

View File

@@ -5,3 +5,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -4,3 +4,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -4,4 +4,5 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
yq -o json -i '.properties = {}' values.schema.json
yq -o json -i '.properties = {}' values.schema.json
../../../hack/update-crd.sh

View File

@@ -9,3 +9,4 @@ get-cloudflare-ips:
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -7,6 +7,7 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
image:
docker buildx build images/grafana \

View File

@@ -4,3 +4,4 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh

View File

@@ -16,8 +16,3 @@ image-cozystack-api:
IMAGE="$(REGISTRY)/cozystack-api:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/cozystack-api.json -o json -r)" \
yq -i '.cozystackAPI.image = strenv(IMAGE)' values.yaml
rm -f images/cozystack-api.json
generate:
rm -rf openapi-schemas
mkdir -p openapi-schemas
find ../../apps ../../extra -maxdepth 2 -name values.schema.json -exec sh -ec 'ln -s ../{} openapi-schemas/$$(basename $$(dirname {})).json' \;

View File

@@ -1 +0,0 @@
../../../extra/bootbox/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/bucket/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/clickhouse/values.schema.json

View File

@@ -1 +0,0 @@
../../../extra/etcd/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/ferretdb/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/http-cache/values.schema.json

View File

@@ -1 +0,0 @@
../../../extra/info/values.schema.json

View File

@@ -1 +0,0 @@
../../../extra/ingress/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/kafka/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/kubernetes/values.schema.json

View File

@@ -1 +0,0 @@
../../../extra/monitoring/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/mysql/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/nats/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/postgres/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/rabbitmq/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/redis/values.schema.json

View File

@@ -1 +0,0 @@
../../../extra/seaweedfs/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/tcp-balancer/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/tenant/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/virtual-machine/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/vm-disk/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/vm-instance/values.schema.json

View File

@@ -1 +0,0 @@
../../../apps/vpn/values.schema.json

View File

@@ -11,3 +11,17 @@ spec:
name: cozystack-api
namespace: cozy-system
version: v1alpha1
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: v1alpha1.core.cozystack.io
spec:
insecureSkipTLSVerify: true
group: core.cozystack.io
groupPriorityMinimum: 1000
versionPriority: 15
service:
name: cozystack-api
namespace: cozy-system
version: v1alpha1

View File

@@ -1,622 +0,0 @@
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: bucket
spec:
application:
kind: Bucket
singular: bucket
plural: buckets
openAPISchema: |
{{- .Files.Get "openapi-schemas/bucket.json" | fromJson | toJson | nindent 6 }}
release:
prefix: bucket-
labels:
cozystack.io/ui: "true"
chart:
name: bucket
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: clickhouse
spec:
application:
kind: ClickHouse
singular: clickhouse
plural: clickhouses
openAPISchema: |
{{- .Files.Get "openapi-schemas/clickhouse.json" | fromJson | toJson | nindent 6 }}
release:
prefix: clickhouse-
labels:
cozystack.io/ui: "true"
chart:
name: clickhouse
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: httpcache
spec:
application:
kind: HTTPCache
singular: httpcache
plural: httpcaches
openAPISchema: |
{{- .Files.Get "openapi-schemas/http-cache.json" | fromJson | toJson | nindent 6 }}
release:
prefix: http-cache-
labels:
cozystack.io/ui: "true"
chart:
name: http-cache
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: nats
spec:
application:
kind: NATS
singular: nats
plural: natses
openAPISchema: |
{{- .Files.Get "openapi-schemas/nats.json" | fromJson | toJson | nindent 6 }}
release:
prefix: nats-
labels:
cozystack.io/ui: "true"
chart:
name: nats
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: tcpbalancer
spec:
application:
kind: TCPBalancer
singular: tcpbalancer
plural: tcpbalancers
openAPISchema: |
{{- .Files.Get "openapi-schemas/tcp-balancer.json" | fromJson | toJson | nindent 6 }}
release:
prefix: tcp-balancer-
labels:
cozystack.io/ui: "true"
chart:
name: tcp-balancer
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: virtualmachine
spec:
application:
kind: VirtualMachine
singular: virtualmachine
plural: virtualmachines
openAPISchema: |
{{- .Files.Get "openapi-schemas/virtual-machine.json" | fromJson | toJson | nindent 6 }}
release:
prefix: virtual-machine-
labels:
cozystack.io/ui: "true"
chart:
name: virtual-machine
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: vpn
spec:
application:
kind: VPN
singular: vpn
plural: vpns
openAPISchema: |
{{- .Files.Get "openapi-schemas/vpn.json" | fromJson | toJson | nindent 6 }}
release:
prefix: vpn-
labels:
cozystack.io/ui: "true"
chart:
name: vpn
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: mysql
spec:
application:
kind: MySQL
singular: mysql
plural: mysqls
openAPISchema: |
{{- .Files.Get "openapi-schemas/mysql.json" | fromJson | toJson | nindent 6 }}
release:
prefix: mysql-
labels:
cozystack.io/ui: "true"
chart:
name: mysql
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: tenant
spec:
application:
kind: Tenant
singular: tenant
plural: tenants
openAPISchema: |
{{- .Files.Get "openapi-schemas/tenant.json" | fromJson | toJson | nindent 6 }}
release:
prefix: tenant-
labels:
cozystack.io/ui: "true"
chart:
name: tenant
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: kubernetes
spec:
application:
kind: Kubernetes
singular: kubernetes
plural: kuberneteses
openAPISchema: |
{{- .Files.Get "openapi-schemas/kubernetes.json" | fromJson | toJson | nindent 6 }}
release:
prefix: kubernetes-
labels:
cozystack.io/ui: "true"
chart:
name: kubernetes
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: redis
spec:
application:
kind: Redis
singular: redis
plural: redises
openAPISchema: |
{{- .Files.Get "openapi-schemas/redis.json" | fromJson | toJson | nindent 6 }}
release:
prefix: redis-
labels:
cozystack.io/ui: "true"
chart:
name: redis
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: rabbitmq
spec:
application:
kind: RabbitMQ
singular: rabbitmq
plural: rabbitmqs
openAPISchema: |
{{- .Files.Get "openapi-schemas/rabbitmq.json" | fromJson | toJson | nindent 6 }}
release:
prefix: rabbitmq-
labels:
cozystack.io/ui: "true"
chart:
name: rabbitmq
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: postgres
spec:
application:
kind: Postgres
singular: postgres
plural: postgreses
openAPISchema: |
{{- .Files.Get "openapi-schemas/postgres.json" | fromJson | toJson | nindent 6 }}
release:
prefix: postgres-
labels:
cozystack.io/ui: "true"
chart:
name: postgres
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
- matchLabels:
cnpg.io/userType: superuser
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: ferretdb
spec:
application:
kind: FerretDB
singular: ferretdb
plural: ferretdbs
openAPISchema: |
{{- .Files.Get "openapi-schemas/ferretdb.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ferretdb-
labels:
cozystack.io/ui: "true"
chart:
name: ferretdb
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: kafka
spec:
application:
kind: Kafka
singular: kafka
plural: kafkas
openAPISchema: |
{{- .Files.Get "openapi-schemas/kafka.json" | fromJson | toJson | nindent 6 }}
release:
prefix: kafka-
labels:
cozystack.io/ui: "true"
chart:
name: kafka
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: vmdisk
spec:
application:
kind: VMDisk
singular: vmdisk
plural: vmdisks
openAPISchema: |
{{- .Files.Get "openapi-schemas/vm-disk.json" | fromJson | toJson | nindent 6 }}
release:
prefix: vm-disk-
labels:
cozystack.io/ui: "true"
chart:
name: vm-disk
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: vminstance
spec:
application:
kind: VMInstance
singular: vminstance
plural: vminstances
openAPISchema: |
{{- .Files.Get "openapi-schemas/vm-instance.json" | fromJson | toJson | nindent 6 }}
release:
prefix: vm-instance-
labels:
cozystack.io/ui: "true"
chart:
name: vm-instance
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: monitoring
spec:
application:
kind: Monitoring
singular: monitoring
plural: monitorings
openAPISchema: |
{{- .Files.Get "openapi-schemas/monitoring.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: monitoring
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: etcd
spec:
application:
kind: Etcd
singular: etcd
plural: etcds
openAPISchema: |
{{- .Files.Get "openapi-schemas/etcd.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: etcd
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: ingress
spec:
application:
kind: Ingress
singular: ingress
plural: ingresses
openAPISchema: |
{{- .Files.Get "openapi-schemas/ingress.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: ingress
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: seaweedfs
spec:
application:
kind: SeaweedFS
singular: seaweedfs
plural: seaweedfses
openAPISchema: |
{{- .Files.Get "openapi-schemas/seaweedfs.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: seaweedfs
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: bootbox
spec:
application:
kind: BootBox
singular: bootbox
plural: bootboxes
openAPISchema: |
{{- .Files.Get "openapi-schemas/bootbox.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: bootbox
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: info
spec:
application:
kind: Info
singular: info
plural: infos
openAPISchema: |
{{- .Files.Get "openapi-schemas/info.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: info
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,36 @@
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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,34 @@
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 is `false`","type":"boolean","default":false},"resticPassword":{"description":"Password for Restic backup encryption","type":"string","default":"<password>"},"s3AccessKey":{"description":"Access key for S3, used for authentication","type":"string","default":"<your-access-key>"},"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, used for authentication","type":"string","default":"<your-secret-key>"},"schedule":{"description":"Cron schedule for automated backups","type":"string","default":"0 2 * * *"}}},"clickhouseKeeper":{"description":"Clickhouse Keeper configuration","type":"object","default":{},"required":["resourcesPreset"],"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 used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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 left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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"},"users":{"description":"Users configuration","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user","type":"string"},"readonly":{"description":"User is `readonly`, default is `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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,34 @@
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":"The number of CPU cores allocated","default":4,"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":"The amount of memory allocated","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}}},"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"}}}
release:
prefix: ""
labels:
cozystack.io/ui: "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
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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,36 @@
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 <ip:port>","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","resources","resourcesPreset"],"properties":{"replicas":{"description":"Number of HAProxy replicas","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each replica. When left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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 for each replica. When left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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"}}}
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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,34 @@
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"
chart:
name: info
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
dashboard:
name: info
category: Administration
singular: Info
plural: Info
description: Info
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX3JhZGlhbF8xNDRfMykiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzE0NF8zKSI+CjxwYXRoIGQ9Ik03Ny42NDA3IDk3LjA4NDRMODIuODMzIDk3LjM2MDRWMTA0LjYzN0g2MS4xNzI4Vjk3LjcxOTdMNjQuMTc3MSA5Ny40NDk1QzY1LjgxMDEgOTcuMjY4NCA2Ni44MTA2IDk2LjcxOTMgNjYuODEwNiA5NC41MzQzVjY5LjIzMTRDNjYuODEwNiA2Ny4yMjE3IDY2LjI3MDEgNjYuNTg2NCA2NC41MzY1IDY2LjU4NjRMNjEuMzU2OCA2Ni40MDgxVjU4Ljg1ODRINzcuNjQ2NUw3Ny42NDA3IDk3LjA4NDRaTTcxLjI3MjYgMzkuMzYzQzc1LjI4MDQgMzkuMzYzIDc4LjE4NyA0Mi4zNzMxIDc4LjE4NyA0Ni4xODgzQzc4LjE4NyA1MC4wMTQ5IDc1LjI3MTggNTIuODM4MSA3MS4xNzc4IDUyLjgzODFDNjYuOTk3NSA1Mi44MzgxIDY0LjI2NjMgNTAuMDE0OSA2NC4yNjYzIDQ2LjE4ODNDNjQuMjY2MyA0Mi4zNzMxIDY2Ljk5NzUgMzkuMzYzIDcxLjI3MjYgMzkuMzYzWk03MiAxMThDNDYuNjM2OCAxMTggMjYgOTcuMzYzMiAyNiA3MkMyNiA0Ni42MzY4IDQ2LjYzNjggMjYgNzIgMjZDOTcuMzU3NSAyNiAxMTggNDYuNjM2OCAxMTggNzJDMTE4IDk3LjM2MzIgOTcuMzU3NSAxMTggNzIgMTE4Wk03MiAzNC42MjVDNTEuMzkyIDM0LjYyNSAzNC42MjUgNTEuMzkyIDM0LjYyNSA3MkMzNC42MjUgOTIuNjA4IDUxLjM5MiAxMDkuMzc1IDcyIDEwOS4zNzVDOTIuNjA4IDEwOS4zNzUgMTA5LjM3NSA5Mi42MDggMTA5LjM3NSA3MkMxMDkuMzc1IDUxLjM5MiA5Mi42MDggMzQuNjI1IDcyIDM0LjYyNVoiIGZpbGw9IndoaXRlIi8+CjwvZz4KPGRlZnM+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQwX3JhZGlhbF8xNDRfMyIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgxLjMyMjk4ZS0wNSAtNy41MDAwMSkgcm90YXRlKDQ0LjcxNzgpIHNjYWxlKDIxNS4zMTcgMzEyLjQ1NSkiPgo8c3RvcCBzdG9wLWNvbG9yPSIjMDBCNUU3Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwMzk4NCIvPgo8L3JhZGlhbEdyYWRpZW50Pgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzE0NF8zIj4KPHJlY3Qgd2lkdGg9IjkyIiBoZWlnaHQ9IjkyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYgMjYpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"]]
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,34 @@
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 left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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"
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
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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,35 @@
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 for each replica. When left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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"}}},"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 for each replica. When left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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"}}}}}
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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
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 (see example)","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true},"resolver":{"description":"Additional resolver configuration to merge into NATS config (see example)","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. Set to `true` (default) to enable Jetstream for persistent messaging in NATS.","type":"boolean","default":true},"size":{"description":"Jetstream persistent storage size. Specifies the size of the persistent storage for Jetstream (message store).","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 left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"storageClass":{"description":"StorageClass used to store the data","type":"string"},"users":{"description":"Users configuration","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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
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 left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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"},"users":{"description":"Users configuration","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user","type":"string"}}}},"vhosts":{"description":"Virtual Hosts configuration","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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,35 @@
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 left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","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"}}}
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", "authEnabled"]]
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
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 it's parent tenant host).","type":"string"},"ingress":{"description":"Deploy own Ingress Controller","type":"boolean","default":false},"isolated":{"description":"Enforce tenant namespace with network policies, `true` by default","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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,36 @@
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 config. See cloud-init documentation for more details: [format](https://cloudinit.readthedocs.io/en/latest/explanation/format.html), [examples](https://cloudinit.readthedocs.io/en/latest/reference/examples.html).","type":"string"},"cloudInitSeed":{"description":"A seed string to generate an SMBIOS UUID for the VM.","type":"string"},"external":{"description":"Enable external access from outside the cluster","type":"boolean","default":false},"externalMethod":{"description":"Specify method to pass through the traffic to the virtual machine. Allowed values: `WholeIP` and `PortList`","type":"string","default":"PortList","enum":["PortList","WholeIP"]},"externalPorts":{"description":"Specify 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 to attach. This should match the GPU resource name in the cluster.","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":"Resources","type":"object","default":{},"properties":{"cpu":{"description":"The number of CPU cores allocated to the virtual machine","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":"The amount of memory allocated to the virtual machine","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":"The number of CPU sockets allocated to the virtual machine (used to define 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":"if the virtual machine should be running","type":"boolean","default":true},"sshKeys":{"description":"List of SSH public keys for authentication. Can be a single key or a list of keys.","type":"array","default":[],"items":{"type":"string"}},"systemDisk":{"description":"System disk configuration","type":"object","default":{},"required":["image","storage"],"properties":{"image":{"description":"The base image for the virtual machine. Allowed values: `ubuntu`, `cirros`, `alpine`, `fedora` and `talos`","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", "gpus"], ["spec", "resources"], ["spec", "sshKeys"], ["spec", "cloudInit"], ["spec", "cloudInitSeed"]]
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,36 @@
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: uploaded as \"golden image\" or from the list: `ubuntu`, `fedora`, `cirros`, `alpine`, and `talos`.","type":"object","required":["name"],"properties":{"name":{"description":"Name of the image to use","type":"string"}}},"upload":{"description":"Upload local image","type":"object"}}},"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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,36 @@
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 config. See cloud-init documentation for more details: [format](https://cloudinit.readthedocs.io/en/latest/explanation/format.html), [examples](https://cloudinit.readthedocs.io/en/latest/reference/examples.html).","type":"string"},"cloudInitSeed":{"description":"A seed string to generate an SMBIOS UUID for the VM.","type":"string"},"disks":{"description":"List of disks to attach","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"bus":{"description":"Disk bus type, such as \"sata\"","type":"string"},"name":{"description":"Disk name","type":"string"}}}},"external":{"description":"Enable external access from outside the cluster","type":"boolean","default":false},"externalMethod":{"description":"Specify method to pass through the traffic to the virtual machine. Allowed values: `WholeIP` and `PortList`","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 (WARN: NVIDIA driver requires at least 4 GiB of RAM)","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"name":{"description":"Name of GPU, such as \"nvidia.com/AD102GL_L40S\"","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":"Resources","type":"object","default":{},"properties":{"cpu":{"description":"The number of CPU cores allocated to the virtual machine","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":"The amount of memory allocated to the virtual machine","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":"The number of CPU sockets allocated to the virtual machine (used to define 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. Can be a single key or a list of keys.","type":"array","default":[],"items":{"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", "gpus"], ["spec", "resources"], ["spec", "sshKeys"], ["spec", "cloudInit"], ["spec", "cloudInitSeed"]]
secrets:
exclude:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -0,0 +1,35 @@
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"},"replicas":{"description":"Number of VPN server replicas","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each VPN server replica. When left empty, 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. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"users":{"description":"Users configuration","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user, autogenerated if none 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:
- matchLabels:
apps.cozystack.io/tenantresource: "false"
include: [{}]

View File

@@ -4,8 +4,11 @@ metadata:
name: cozystack-api
rules:
- apiGroups: [""]
resources: ["namespaces"]
resources: ["namespaces", "secrets"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create", "update", "patch", "delete"]
- apiGroups: ["admissionregistration.k8s.io"]
resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations", "validatingadmissionpolicies", "validatingadmissionpolicybindings"]
verbs: ["get", "watch", "list"]

View File

@@ -0,0 +1,26 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tenantnamespaces-read
rules:
- apiGroups:
- core.cozystack.io
resources:
- tenantnamespaces
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tenantnamespaces-read-authenticated
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tenantnamespaces-read
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:authenticated

View File

@@ -1,2 +1,2 @@
cozystackAPI:
image: ghcr.io/cozystack/cozystack/cozystack-api:latest@sha256:be6aca4df4b769538454d6c65b7045b2bef81b2a15d70eeddbfdf55d6bd17d6c
image: ghcr.io/cozystack/cozystack/cozystack-api:latest@sha256:73fd18b7dcc1782002574d3b6436f5f164d43c7fb0e78cdcf2d71fe10f9863fd

View File

@@ -12,7 +12,7 @@ spec:
listKind: CozystackResourceDefinitionList
plural: cozystackresourcedefinitions
singular: cozystackresourcedefinition
scope: Namespaced
scope: Cluster
versions:
- name: v1alpha1
schema:
@@ -62,6 +62,67 @@ spec:
- plural
- singular
type: object
dashboard:
description: Dashboard configuration for this resource
properties:
category:
description: Category used to group resources in the UI (e.g.,
"Storage", "Networking")
type: string
description:
description: Short description shown in catalogs or headers (e.g.,
"S3 compatible storage")
type: string
icon:
description: Icon encoded as a string (e.g., inline SVG, base64,
or data URI)
type: string
keysOrder:
description: Order of keys in the YAML view
items:
items:
type: string
type: array
type: array
name:
description: Hard-coded name used in the UI (e.g., "bucket")
type: string
plural:
description: Plural human-readable name (e.g., "Buckets")
type: string
singular:
description: Human-readable name shown in the UI (e.g., "Bucket")
type: string
singularResource:
description: Whether this resource is singular (not a collection)
in the UI
type: boolean
tabs:
description: Which tabs to show for this resource
items:
description: DashboardTab enumerates allowed UI tabs.
enum:
- workloads
- ingresses
- services
- secrets
- yaml
type: string
type: array
tags:
description: Free-form tags for search and filtering
items:
type: string
type: array
weight:
description: Order weight for sorting resources in the UI (lower
first)
type: integer
required:
- category
- plural
- singular
type: object
release:
description: Release configuration
properties:

View File

@@ -0,0 +1,118 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.4
name: breadcrumbs.dashboard.cozystack.io
spec:
group: dashboard.cozystack.io
names:
kind: Breadcrumb
listKind: BreadcrumbList
plural: breadcrumbs
singular: breadcrumb
scope: Cluster
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
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: |-
ArbitrarySpec holds schemaless user data and preserves unknown fields.
We map the entire .spec to a single JSON payload to mirror the CRDs you provided.
NOTE: Using apiextensionsv1.JSON avoids losing arbitrary structure during round-trips.
type: object
x-kubernetes-preserve-unknown-fields: true
status:
description: CommonStatus is a generic Status block with Kubernetes conditions.
properties:
conditions:
description: Conditions represent the latest available observations
of an object'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
observedGeneration:
description: ObservedGeneration reflects the most recent generation
observed by the controller.
format: int64
type: integer
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

Some files were not shown because too many files have changed in this diff Show More