mirror of
https://github.com/cozystack/cozystack.git
synced 2026-03-12 01:48:58 +00:00
Compare commits
1 Commits
foundation
...
lineage-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca61c71084 |
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
- name: Install generate
|
||||
run: |
|
||||
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v0.9.0/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen
|
||||
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v0.8.5/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen
|
||||
|
||||
- name: Run pre-commit hooks
|
||||
run: |
|
||||
|
||||
@@ -30,5 +30,3 @@ This list is sorted in chronological order, based on the submission date.
|
||||
| [Bootstack](https://bootstack.app/) | @mrkhachaturov | 2024-08-01| At Bootstack, we utilize a Kubernetes operator specifically designed to simplify and streamline cloud infrastructure creation.|
|
||||
| [gohost](https://gohost.kz/) | @karabass_off | 2024-02-01 | Our company has been working in the market of Kazakhstan for more than 15 years, providing clients with a standard set of services: VPS/VDC, IaaS, shared hosting, etc. Now we are expanding the lineup by introducing Bare Metal Kubenetes cluster under Cozystack management. |
|
||||
| [Urmanac](https://urmanac.com) | @kingdonb | 2024-12-04 | Urmanac is the future home of a hosting platform for the knowledge base of a community of personal server enthusiasts. We use Cozystack to provide support services for web sites hosted using both conventional deployments and on SpinKube, with WASM. |
|
||||
| [Hidora](https://hikube.cloud) | @matthieu-robin | 2025-09-17 | Hidora is a Swiss cloud provider delivering managed services and infrastructure solutions through datacenters located in Switzerland, ensuring data sovereignty and reliability. Its sovereign cloud platform, Hikube, is designed to run workloads with high availability across multiple datacenters, providing enterprises with a secure and scalable foundation for their applications based on Cozystack. |
|
||||
|
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
|
||||
cozystackiov1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
"github.com/cozystack/cozystack/internal/controller"
|
||||
"github.com/cozystack/cozystack/internal/controller/lineagelabeler"
|
||||
"github.com/cozystack/cozystack/internal/telemetry"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
@@ -67,6 +68,7 @@ func main() {
|
||||
var telemetryEndpoint string
|
||||
var telemetryInterval string
|
||||
var cozystackVersion string
|
||||
var watchResources string
|
||||
var tlsOpts []func(*tls.Config)
|
||||
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
|
||||
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
|
||||
@@ -86,6 +88,9 @@ func main() {
|
||||
"Interval between telemetry data collection (e.g. 15m, 1h)")
|
||||
flag.StringVar(&cozystackVersion, "cozystack-version", "unknown",
|
||||
"Version of Cozystack")
|
||||
flag.StringVar(&watchResources, "watch-resources",
|
||||
"v1/Pod,v1/Service,v1/Secret,v1/PersistentVolumeClaim",
|
||||
"Comma-separated list of resources to watch in the form 'group/version/Kind'.")
|
||||
opts := zap.Options{
|
||||
Development: false,
|
||||
}
|
||||
@@ -214,6 +219,15 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := (&lineagelabeler.LineageLabelerReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
WatchResourceCSV: watchResources,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "LineageLabeler")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
|
||||
367
internal/controller/lineagelabeler/lineage_labeler_controller.go
Normal file
367
internal/controller/lineagelabeler/lineage_labeler_controller.go
Normal file
@@ -0,0 +1,367 @@
|
||||
package lineagelabeler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
"github.com/cozystack/cozystack/internal/shared/crdmem"
|
||||
"github.com/cozystack/cozystack/pkg/lineage"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/discovery/cached/memory"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var ErrNoAncestors = errors.New("no ancestors")
|
||||
|
||||
type LineageLabelerReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
WatchResourceCSV string
|
||||
|
||||
dynClient dynamic.Interface
|
||||
mapper meta.RESTMapper
|
||||
|
||||
appMap atomic.Value
|
||||
once sync.Once
|
||||
|
||||
mem *crdmem.Memory
|
||||
}
|
||||
|
||||
type chartRef struct{ repo, chart string }
|
||||
type appRef struct{ groupVersion, kind, prefix string }
|
||||
|
||||
func (r *LineageLabelerReconciler) initMapping() {
|
||||
r.once.Do(func() {
|
||||
r.appMap.Store(make(map[chartRef]appRef))
|
||||
})
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) currentMap() map[chartRef]appRef {
|
||||
val := r.appMap.Load()
|
||||
if val == nil {
|
||||
return map[chartRef]appRef{}
|
||||
}
|
||||
return val.(map[chartRef]appRef)
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
|
||||
cfg := r.currentMap()
|
||||
s := hr.Spec.Chart.Spec
|
||||
key := chartRef{s.SourceRef.Name, s.Chart}
|
||||
if v, ok := cfg[key]; ok {
|
||||
return v.groupVersion, v.kind, v.prefix, nil
|
||||
}
|
||||
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
|
||||
}
|
||||
|
||||
func parseGVKList(csv string) ([]schema.GroupVersionKind, error) {
|
||||
csv = strings.TrimSpace(csv)
|
||||
if csv == "" {
|
||||
return nil, fmt.Errorf("watch resource list is empty")
|
||||
}
|
||||
parts := strings.Split(csv, ",")
|
||||
out := make([]schema.GroupVersionKind, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
s := strings.Split(p, "/")
|
||||
if len(s) == 2 {
|
||||
out = append(out, schema.GroupVersionKind{Group: "", Version: s[0], Kind: s[1]})
|
||||
continue
|
||||
}
|
||||
if len(s) == 3 {
|
||||
out = append(out, schema.GroupVersionKind{Group: s[0], Version: s[1], Kind: s[2]})
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("invalid resource token %q, expected 'group/version/Kind' or 'v1/Kind'", p)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
r.initMapping()
|
||||
|
||||
cfg := rest.CopyConfig(mgr.GetConfig())
|
||||
dc, err := dynamic.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cached := memory.NewMemCacheClient(disco)
|
||||
r.dynClient = dc
|
||||
r.mapper = restmapper.NewDeferredDiscoveryRESTMapper(cached)
|
||||
|
||||
if r.mem == nil {
|
||||
r.mem = crdmem.Global()
|
||||
}
|
||||
if err := r.mem.EnsurePrimingWithManager(mgr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gvks, err := parseGVKList(r.WatchResourceCSV)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(gvks) == 0 {
|
||||
return fmt.Errorf("no resources to watch")
|
||||
}
|
||||
|
||||
b := ctrl.NewControllerManagedBy(mgr).Named("lineage-labeler")
|
||||
|
||||
nsPred := predicate.NewPredicateFuncs(func(obj client.Object) bool {
|
||||
ns := obj.GetNamespace()
|
||||
return ns != "" && strings.HasPrefix(ns, "tenant-")
|
||||
})
|
||||
|
||||
primary := gvks[0]
|
||||
primaryObj := &unstructured.Unstructured{}
|
||||
primaryObj.SetGroupVersionKind(primary)
|
||||
b = b.For(primaryObj,
|
||||
builder.WithPredicates(
|
||||
predicate.And(
|
||||
nsPred,
|
||||
predicate.Or(
|
||||
predicate.GenerationChangedPredicate{},
|
||||
predicate.ResourceVersionChangedPredicate{},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
for _, gvk := range gvks[1:] {
|
||||
u := &unstructured.Unstructured{}
|
||||
u.SetGroupVersionKind(gvk)
|
||||
b = b.Watches(u,
|
||||
&handler.EnqueueRequestForObject{},
|
||||
builder.WithPredicates(
|
||||
predicate.And(
|
||||
nsPred,
|
||||
predicate.Or(
|
||||
predicate.GenerationChangedPredicate{},
|
||||
predicate.ResourceVersionChangedPredicate{},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
b = b.Watches(
|
||||
&cozyv1alpha1.CozystackResourceDefinition{},
|
||||
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
_ = r.refreshAppMap(ctx)
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
|
||||
_ = r.refreshAppMap(context.Background())
|
||||
|
||||
return b.Complete(r)
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) refreshAppMap(ctx context.Context) error {
|
||||
var items []cozyv1alpha1.CozystackResourceDefinition
|
||||
var err error
|
||||
if r.mem != nil {
|
||||
items, err = r.mem.ListFromCacheOrAPI(ctx, r.Client)
|
||||
} else {
|
||||
var list cozyv1alpha1.CozystackResourceDefinitionList
|
||||
err = r.Client.List(ctx, &list)
|
||||
items = list.Items
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newMap := make(map[chartRef]appRef, len(items))
|
||||
for _, crd := range items {
|
||||
k := chartRef{
|
||||
repo: crd.Spec.Release.Chart.SourceRef.Name,
|
||||
chart: crd.Spec.Release.Chart.Name,
|
||||
}
|
||||
v := appRef{
|
||||
groupVersion: "apps.cozystack.io/v1alpha1",
|
||||
kind: crd.Spec.Application.Kind,
|
||||
prefix: crd.Spec.Release.Prefix,
|
||||
}
|
||||
if _, exists := newMap[k]; exists {
|
||||
continue
|
||||
}
|
||||
newMap[k] = v
|
||||
}
|
||||
r.appMap.Store(newMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
if req.Namespace == "" || !strings.HasPrefix(req.Namespace, "tenant-") {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if len(r.currentMap()) == 0 {
|
||||
_ = r.refreshAppMap(ctx)
|
||||
if len(r.currentMap()) == 0 {
|
||||
return ctrl.Result{RequeueAfter: 2 * time.Second}, nil
|
||||
}
|
||||
}
|
||||
|
||||
gvks, err := parseGVKList(r.WatchResourceCSV)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
var obj *unstructured.Unstructured
|
||||
found := false
|
||||
|
||||
for _, gvk := range gvks {
|
||||
mapping, mErr := r.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if mErr != nil {
|
||||
continue
|
||||
}
|
||||
ns := req.Namespace
|
||||
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
|
||||
ns = ""
|
||||
}
|
||||
res, gErr := r.dynClient.Resource(mapping.Resource).Namespace(ns).Get(ctx, req.Name, metav1.GetOptions{})
|
||||
if gErr != nil {
|
||||
if apierrors.IsNotFound(gErr) {
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
obj = res
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
if !found || obj == nil {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
existing := obj.GetLabels()
|
||||
if existing == nil {
|
||||
existing = map[string]string{}
|
||||
}
|
||||
|
||||
keys := []string{
|
||||
"apps.cozystack.io/application.group",
|
||||
"apps.cozystack.io/application.kind",
|
||||
"apps.cozystack.io/application.name",
|
||||
}
|
||||
allPresent := true
|
||||
for _, k := range keys {
|
||||
if _, ok := existing[k]; !ok {
|
||||
allPresent = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allPresent {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
labels, warn, err := r.computeLabels(ctx, obj)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNoAncestors) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
if warn != "" {
|
||||
l.V(1).Info("lineage ambiguous; using first ancestor", "name", req.NamespacedName)
|
||||
}
|
||||
|
||||
for k, v := range labels {
|
||||
existing[k] = v
|
||||
}
|
||||
obj.SetLabels(existing)
|
||||
|
||||
// Server-Side Apply: claim ownership of our label keys
|
||||
gvk := obj.GroupVersionKind()
|
||||
patch := &unstructured.Unstructured{}
|
||||
patch.SetGroupVersionKind(gvk)
|
||||
patch.SetNamespace(obj.GetNamespace())
|
||||
patch.SetName(obj.GetName())
|
||||
patch.SetLabels(map[string]string{
|
||||
"apps.cozystack.io/application.group": existing["apps.cozystack.io/application.group"],
|
||||
"apps.cozystack.io/application.kind": existing["apps.cozystack.io/application.kind"],
|
||||
"apps.cozystack.io/application.name": existing["apps.cozystack.io/application.name"],
|
||||
})
|
||||
|
||||
// Use controller-runtime client with Apply patch type and field owner
|
||||
if err := r.Patch(ctx, patch,
|
||||
client.Apply,
|
||||
client.FieldOwner("cozystack/lineage"),
|
||||
client.ForceOwnership(false),
|
||||
); err != nil {
|
||||
if apierrors.IsConflict(err) {
|
||||
return ctrl.Result{RequeueAfter: 500 * time.Millisecond}, nil
|
||||
}
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) computeLabels(ctx context.Context, o *unstructured.Unstructured) (map[string]string, string, error) {
|
||||
owners := lineage.WalkOwnershipGraph(ctx, r.dynClient, r.mapper, r, o)
|
||||
if len(owners) == 0 {
|
||||
return nil, "", ErrNoAncestors
|
||||
}
|
||||
obj, err := owners[0].GetUnstructured(ctx, r.dynClient, r.mapper)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
gv, err := schema.ParseGroupVersion(obj.GetAPIVersion())
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("invalid APIVersion %s: %w", obj.GetAPIVersion(), err)
|
||||
}
|
||||
var warn string
|
||||
if len(owners) > 1 {
|
||||
warn = "ambiguous"
|
||||
}
|
||||
group := gv.Group
|
||||
if len(group) > 63 {
|
||||
group = trimDNSLabel(group[:63])
|
||||
}
|
||||
return map[string]string{
|
||||
"apps.cozystack.io/application.group": group,
|
||||
"apps.cozystack.io/application.kind": obj.GetKind(),
|
||||
"apps.cozystack.io/application.name": obj.GetName(),
|
||||
}, warn, nil
|
||||
}
|
||||
|
||||
func trimDNSLabel(s string) string {
|
||||
for len(s) > 0 {
|
||||
b := s[len(s)-1]
|
||||
if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') {
|
||||
return s
|
||||
}
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -27,7 +27,7 @@ For more details, read [Restic: Effective Backup from Stdin](https://blog.aenix.
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of Clickhouse replicas | `int` | `2` |
|
||||
| `shards` | Number of Clickhouse shards | `int` | `1` |
|
||||
| `resources` | Explicit CPU and memory configuration for each Clickhouse replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each Clickhouse replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
@@ -65,7 +65,7 @@ For more details, read [Restic: Effective Backup from Stdin](https://blog.aenix.
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `clickhouseKeeper` | Clickhouse Keeper configuration | `*object` | `null` |
|
||||
| `clickhouseKeeper` | Clickhouse Keeper configuration | `*object` | `{}` |
|
||||
| `clickhouseKeeper.enabled` | Deploy ClickHouse Keeper for cluster coordination | `*bool` | `true` |
|
||||
| `clickhouseKeeper.size` | Persistent Volume Claim size, available for application data | `*quantity` | `1Gi` |
|
||||
| `clickhouseKeeper.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
|
||||
@@ -5,7 +5,16 @@
|
||||
"backup": {
|
||||
"description": "Backup configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"cleanupStrategy": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m",
|
||||
"enabled": false,
|
||||
"resticPassword": "\u003cpassword\u003e",
|
||||
"s3AccessKey": "\u003cyour-access-key\u003e",
|
||||
"s3Bucket": "s3.example.org/clickhouse-backups",
|
||||
"s3Region": "us-east-1",
|
||||
"s3SecretKey": "\u003cyour-secret-key\u003e",
|
||||
"schedule": "0 2 * * *"
|
||||
},
|
||||
"required": [
|
||||
"cleanupStrategy",
|
||||
"enabled",
|
||||
@@ -62,7 +71,12 @@
|
||||
"clickhouseKeeper": {
|
||||
"description": "Clickhouse Keeper configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": true,
|
||||
"replicas": 3,
|
||||
"resourcesPreset": "micro",
|
||||
"size": "1Gi"
|
||||
},
|
||||
"required": [
|
||||
"resourcesPreset"
|
||||
],
|
||||
|
||||
@@ -11,7 +11,7 @@ Internally, FerretDB service is backed by Postgres.
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of replicas | `int` | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"backup": {
|
||||
"description": "Backup configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"destinationPath": "s3://bucket/path/to/folder/",
|
||||
"enabled": false,
|
||||
"endpointURL": "http://minio-gateway-service:9000",
|
||||
"retentionPolicy": "30d",
|
||||
"s3AccessKey": "\u003cyour-access-key\u003e",
|
||||
"s3SecretKey": "\u003cyour-secret-key\u003e",
|
||||
"schedule": "0 2 * * * *"
|
||||
},
|
||||
"required": [
|
||||
"destinationPath",
|
||||
"enabled",
|
||||
@@ -56,7 +64,11 @@
|
||||
"bootstrap": {
|
||||
"description": "Bootstrap (recovery) configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"oldName": "",
|
||||
"recoveryTime": ""
|
||||
},
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Restore database cluster from a backup",
|
||||
@@ -81,7 +93,10 @@
|
||||
"quorum": {
|
||||
"description": "Configuration for the quorum-based synchronous replication",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"maxSyncReplicas": 0,
|
||||
"minSyncReplicas": 0
|
||||
},
|
||||
"required": [
|
||||
"maxSyncReplicas",
|
||||
"minSyncReplicas"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<svg width="144" height="144" viewBox="0 0 144 144" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="144" height="144" rx="24" fill="url(#paint0_radial_858_3072)"/>
|
||||
<path d="M135.784 75.6446L135.939 87.7638L89.6846 81.5362L62.0868 84.5079L35.3417 81.4329L8.75167 84.5854L8.72583 81.5104L35.3676 77.5826V64.1713L62.2935 70.7348L62.3452 81.2778L89.4779 77.686L89.4004 64.1972L135.784 75.6446Z" fill="white"/>
|
||||
<path d="M89.4778 86.0325L135.888 90.8388V102.726H8.64825L8.51904 99.573H35.2641C35.2641 99.573 35.2641 90.7355 35.2641 86.0583C44.2567 86.9369 62.0867 88.6941 62.0867 88.6941V99.2629H89.4778V86.0325Z" fill="white"/>
|
||||
<path d="M62.2934 66.8846L62.2158 63.6286C62.2158 63.6286 79.8133 58.3571 88.9092 55.6697C88.9092 51.3026 88.9092 47.0906 88.9092 42C104.879 48.4085 120.228 54.6102 135.733 60.8378C135.733 64.7139 135.733 68.435 135.733 72.5695C119.841 68.2024 104.284 63.9129 89.1676 59.7525C79.9684 62.2074 62.2934 66.8846 62.2934 66.8846Z" fill="white"/>
|
||||
<path d="M35.3962 81.7073L8.80612 84.8598L8.78027 81.7848L35.422 77.857V64.4457L62.348 71.0093L62.3996 81.5522L89.5323 77.9604L89.4548 64.4716L135.839 75.919L135.994 88.0382L89.7391 81.8106L62.1412 84.7823L35.3962 81.7073Z" fill="white"/>
|
||||
<path d="M89.5323 86.3069L135.942 91.1133V103H8.7027L8.57349 99.8474H35.3186C35.3186 99.8474 35.3186 91.0099 35.3186 86.3328C44.3111 87.2114 62.1412 88.9685 62.1412 88.9685V99.5373H89.5323V86.3069Z" fill="white"/>
|
||||
<path d="M62.3483 67.159L62.2708 63.9031C62.2708 63.9031 79.8682 58.6316 88.9642 55.9442C88.9642 51.5771 88.9642 47.3651 88.9642 42.2744C104.934 48.6829 120.283 54.8847 135.787 61.1123C135.787 64.9884 135.787 68.7094 135.787 72.8439C119.895 68.4769 104.339 64.1873 89.2226 60.027C80.0233 62.4818 62.3483 67.159 62.3483 67.159Z" fill="white"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_858_3072" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-29.5 -18) rotate(39.6963) scale(302.168 275.271)">
|
||||
<stop stop-color="#BEDDFF"/>
|
||||
<stop offset="0.259615" stop-color="#9ECCFD"/>
|
||||
<stop offset="0.591346" stop-color="#3F9AFB"/>
|
||||
<stop offset="1" stop-color="#0B70E0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.7.0@sha256:50ac1581e3100bd6c477a71161cb455a341ffaf9e5e2f6086802e4e25271e8af
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.7.0@sha256:e0a07082bb6fc6aeaae2315f335386f1705a646c72f9e0af512aebbca5cb2b15
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
"haproxy": {
|
||||
"description": "HAProxy configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "nano"
|
||||
},
|
||||
"required": [
|
||||
"replicas",
|
||||
"resources",
|
||||
@@ -82,7 +86,11 @@
|
||||
"nginx": {
|
||||
"description": "Nginx configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "nano"
|
||||
},
|
||||
"required": [
|
||||
"replicas",
|
||||
"resourcesPreset"
|
||||
|
||||
@@ -10,7 +10,13 @@
|
||||
"kafka": {
|
||||
"description": "Kafka configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"replicas": 3,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"size": "10Gi",
|
||||
"storageClass": ""
|
||||
},
|
||||
"required": [
|
||||
"replicas",
|
||||
"resourcesPreset",
|
||||
@@ -126,7 +132,13 @@
|
||||
"zookeeper": {
|
||||
"description": "Zookeeper configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"replicas": 3,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"size": "5Gi",
|
||||
"storageClass": ""
|
||||
},
|
||||
"required": [
|
||||
"replicas",
|
||||
"resourcesPreset",
|
||||
|
||||
@@ -16,7 +16,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.29.1
|
||||
version: 0.29.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
@@ -91,21 +91,21 @@ See the reference for components utilized in this service:
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------- | ----------- |
|
||||
| `version` | Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33. | `string` | `v1.33` |
|
||||
| `host` | Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty. | `string` | `""` |
|
||||
| `nodeGroups` | Worker nodes configuration | `map[string]object` | `{...}` |
|
||||
| `nodeGroups[name].minReplicas` | Minimum amount of replicas | `int` | `0` |
|
||||
| `nodeGroups[name].maxReplicas` | Maximum amount of replicas | `int` | `10` |
|
||||
| `nodeGroups[name].instanceType` | Virtual machine instance type | `string` | `u1.medium` |
|
||||
| `nodeGroups[name].ephemeralStorage` | Ephemeral storage size | `quantity` | `20Gi` |
|
||||
| `nodeGroups[name].roles` | List of node's roles | `[]string` | `[]` |
|
||||
| `nodeGroups[name].resources` | Resources available to each worker node | `object` | `{}` |
|
||||
| `nodeGroups[name].resources.cpu` | CPU available to each worker node | `*quantity` | `null` |
|
||||
| `nodeGroups[name].resources.memory` | Memory (RAM) available to each worker node | `*quantity` | `null` |
|
||||
| `nodeGroups[name].gpus` | List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM) | `[]object` | `{}` |
|
||||
| `nodeGroups[name].gpus[i].name` | Name of GPU, such as "nvidia.com/AD102GL_L40S" | `string` | `""` |
|
||||
| Name | Description | Type | Value |
|
||||
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| `version` | Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33. | `string` | `v1.33` |
|
||||
| `host` | Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty. | `string` | `""` |
|
||||
| `nodeGroups` | Worker nodes configuration | `map[string]object` | `{...}` |
|
||||
| `nodeGroups[name].minReplicas` | Minimum amount of replicas | `int` | `0` |
|
||||
| `nodeGroups[name].maxReplicas` | Maximum amount of replicas | `int` | `0` |
|
||||
| `nodeGroups[name].instanceType` | Virtual machine instance type | `string` | `""` |
|
||||
| `nodeGroups[name].ephemeralStorage` | Ephemeral storage size | `quantity` | `""` |
|
||||
| `nodeGroups[name].roles` | List of node's roles | `[]string` | `[]` |
|
||||
| `nodeGroups[name].resources` | Resources available to each worker node | `object` | `{}` |
|
||||
| `nodeGroups[name].resources.cpu` | CPU available to each worker node | `*quantity` | `null` |
|
||||
| `nodeGroups[name].resources.memory` | Memory (RAM) available to each worker node | `*quantity` | `null` |
|
||||
| `nodeGroups[name].gpus` | List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM) | `[]object` | `[]` |
|
||||
| `nodeGroups[name].gpus.name` | Name of GPU, such as "nvidia.com/AD102GL_L40S" | `string` | `""` |
|
||||
|
||||
|
||||
### Cluster Addons
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.29.1@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.29.0@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.29.1@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.29.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.29.1@sha256:cae43eae09fc39e5f2140d30ef55253f871cc565b8b7a564a54077b7cbd92212
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.29.0@sha256:3a3bc912f70ccba1e9f92a0754179dbdc4c01f24073467b6d1406c77da794863
|
||||
|
||||
@@ -5,7 +5,46 @@
|
||||
"addons": {
|
||||
"description": "Cluster addons configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"certManager": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"cilium": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"coredns": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"fluxcd": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"gatewayAPI": {
|
||||
"enabled": false
|
||||
},
|
||||
"gpuOperator": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"ingressNginx": {
|
||||
"enabled": false,
|
||||
"exposeMethod": "Proxied",
|
||||
"hosts": {},
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"monitoringAgents": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"velero": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"verticalPodAutoscaler": {
|
||||
"valuesOverride": {}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"certManager",
|
||||
"cilium",
|
||||
@@ -22,7 +61,10 @@
|
||||
"certManager": {
|
||||
"description": "Cert-manager: automatically creates and manages SSL/TLS certificate",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
@@ -44,7 +86,9 @@
|
||||
"cilium": {
|
||||
"description": "Cilium CNI plugin",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"valuesOverride"
|
||||
],
|
||||
@@ -60,7 +104,9 @@
|
||||
"coredns": {
|
||||
"description": "Coredns",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"valuesOverride"
|
||||
],
|
||||
@@ -76,7 +122,10 @@
|
||||
"fluxcd": {
|
||||
"description": "Flux CD",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
@@ -98,7 +147,9 @@
|
||||
"gatewayAPI": {
|
||||
"description": "Gateway API",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false
|
||||
},
|
||||
"required": [
|
||||
"enabled"
|
||||
],
|
||||
@@ -113,7 +164,10 @@
|
||||
"gpuOperator": {
|
||||
"description": "GPU-operator: NVIDIA GPU Operator",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
@@ -135,7 +189,12 @@
|
||||
"ingressNginx": {
|
||||
"description": "Ingress-NGINX Controller",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"exposeMethod": "Proxied",
|
||||
"hosts": {},
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"exposeMethod",
|
||||
@@ -175,7 +234,10 @@
|
||||
"monitoringAgents": {
|
||||
"description": "MonitoringAgents",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
@@ -197,7 +259,10 @@
|
||||
"velero": {
|
||||
"description": "Velero",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
@@ -219,7 +284,9 @@
|
||||
"verticalPodAutoscaler": {
|
||||
"description": "VerticalPodAutoscaler",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"valuesOverride"
|
||||
],
|
||||
@@ -237,7 +304,27 @@
|
||||
"controlPlane": {
|
||||
"description": "Control Plane Configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"apiServer": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "medium"
|
||||
},
|
||||
"controllerManager": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
},
|
||||
"konnectivity": {
|
||||
"server": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
}
|
||||
},
|
||||
"replicas": 2,
|
||||
"scheduler": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"apiServer",
|
||||
"controllerManager",
|
||||
@@ -249,7 +336,10 @@
|
||||
"apiServer": {
|
||||
"description": "Control plane API server configuration.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "medium"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
@@ -307,7 +397,10 @@
|
||||
"controllerManager": {
|
||||
"description": "Controller Manager configuration.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
@@ -365,7 +458,12 @@
|
||||
"konnectivity": {
|
||||
"description": "Konnectivity configuration.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"server": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"server"
|
||||
],
|
||||
@@ -373,7 +471,10 @@
|
||||
"server": {
|
||||
"description": "Konnectivity server configuration.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
@@ -438,7 +539,10 @@
|
||||
"scheduler": {
|
||||
"description": "Scheduler configuration.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
@@ -505,7 +609,7 @@
|
||||
"default": {
|
||||
"md0": {
|
||||
"ephemeralStorage": "20Gi",
|
||||
"gpus": [],
|
||||
"gpus": {},
|
||||
"instanceType": "u1.medium",
|
||||
"maxReplicas": 10,
|
||||
"minReplicas": 0,
|
||||
@@ -527,7 +631,6 @@
|
||||
"properties": {
|
||||
"ephemeralStorage": {
|
||||
"description": "Ephemeral storage size",
|
||||
"default": "20Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -542,7 +645,6 @@
|
||||
"gpus": {
|
||||
"description": "List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM)",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -558,23 +660,19 @@
|
||||
},
|
||||
"instanceType": {
|
||||
"description": "Virtual machine instance type",
|
||||
"type": "string",
|
||||
"default": "u1.medium"
|
||||
"type": "string"
|
||||
},
|
||||
"maxReplicas": {
|
||||
"description": "Maximum amount of replicas",
|
||||
"type": "integer",
|
||||
"default": 10
|
||||
"type": "integer"
|
||||
},
|
||||
"minReplicas": {
|
||||
"description": "Minimum amount of replicas",
|
||||
"type": "integer",
|
||||
"default": 0
|
||||
"type": "integer"
|
||||
},
|
||||
"resources": {
|
||||
"description": "Resources available to each worker node",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each worker node",
|
||||
|
||||
@@ -9,17 +9,17 @@ version: "v1.33"
|
||||
## @param host {string} Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty.
|
||||
host: ""
|
||||
|
||||
## @param nodeGroups {map[string]nodeGroup} Worker nodes configuration
|
||||
## @field nodeGroup {nodeGroup} Node configuration
|
||||
## @field nodeGroup.minReplicas {int default=0} Minimum amount of replicas
|
||||
## @field nodeGroup.maxReplicas {int default=10} Maximum amount of replicas
|
||||
## @field nodeGroup.instanceType {string default="u1.medium"} Virtual machine instance type
|
||||
## @field nodeGroup.ephemeralStorage {quantity default="20Gi"} Ephemeral storage size
|
||||
## @field nodeGroup.roles {[]string default=[]} List of node's roles
|
||||
## @field nodeGroup.resources {resources default={}} Resources available to each worker node
|
||||
## @param nodeGroups {map[string]node} Worker nodes configuration
|
||||
## @field node {node} Node configuration
|
||||
## @field node.minReplicas {int} Minimum amount of replicas
|
||||
## @field node.maxReplicas {int} Maximum amount of replicas
|
||||
## @field node.instanceType {string} Virtual machine instance type
|
||||
## @field node.ephemeralStorage {quantity} Ephemeral storage size
|
||||
## @field node.roles {[]string} List of node's roles
|
||||
## @field node.resources {resources} Resources available to each worker node
|
||||
## @field resources.cpu {*quantity} CPU available to each worker node
|
||||
## @field resources.memory {*quantity} Memory (RAM) available to each worker node
|
||||
## @field nodeGroup.gpus {[]gpu default={}} List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM)
|
||||
## @field node.gpus {[]gpu} List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM)
|
||||
## @field gpu.name {string} Name of GPU, such as "nvidia.com/AD102GL_L40S"
|
||||
##
|
||||
nodeGroups:
|
||||
|
||||
@@ -72,7 +72,7 @@ more details:
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of MariaDB replicas | `int` | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each MariaDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each MariaDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
|
||||
|
||||
@@ -5,7 +5,16 @@
|
||||
"backup": {
|
||||
"description": "Backup configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"cleanupStrategy": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m",
|
||||
"enabled": false,
|
||||
"resticPassword": "\u003cpassword\u003e",
|
||||
"s3AccessKey": "\u003cyour-access-key\u003e",
|
||||
"s3Bucket": "s3.example.org/mysql-backups",
|
||||
"s3Region": "us-east-1",
|
||||
"s3SecretKey": "\u003cyour-secret-key\u003e",
|
||||
"schedule": "0 2 * * *"
|
||||
},
|
||||
"required": [
|
||||
"cleanupStrategy",
|
||||
"enabled",
|
||||
|
||||
@@ -10,7 +10,7 @@ It provides a data layer for cloud native applications, IoT messaging, and micro
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of replicas | `int` | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each NATS replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each NATS replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
"config": {
|
||||
"description": "NATS configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"merge": {},
|
||||
"resolver": {}
|
||||
},
|
||||
"properties": {
|
||||
"merge": {
|
||||
"description": "Additional configuration to merge into NATS config (see example)",
|
||||
@@ -29,7 +32,10 @@
|
||||
"jetstream": {
|
||||
"description": "Jetstream configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": true,
|
||||
"size": "10Gi"
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"size"
|
||||
|
||||
@@ -69,7 +69,7 @@ See:
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of Postgres replicas | `int` | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each PostgreSQL replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each PostgreSQL replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
|
||||
@@ -5,7 +5,15 @@
|
||||
"backup": {
|
||||
"description": "Backup configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"destinationPath": "s3://bucket/path/to/folder/",
|
||||
"enabled": false,
|
||||
"endpointURL": "http://minio-gateway-service:9000",
|
||||
"retentionPolicy": "30d",
|
||||
"s3AccessKey": "\u003cyour-access-key\u003e",
|
||||
"s3SecretKey": "\u003cyour-secret-key\u003e",
|
||||
"schedule": "0 2 * * * *"
|
||||
},
|
||||
"properties": {
|
||||
"destinationPath": {
|
||||
"description": "Path to store the backup (i.e. s3://bucket/path/to/folder)",
|
||||
@@ -47,7 +55,11 @@
|
||||
"bootstrap": {
|
||||
"description": "Bootstrap configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"oldName": "",
|
||||
"recoveryTime": ""
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"oldName"
|
||||
@@ -113,7 +125,11 @@
|
||||
"postgresql": {
|
||||
"description": "PostgreSQL server configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"parameters": {
|
||||
"max_connections": 100
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"parameters"
|
||||
],
|
||||
@@ -121,7 +137,9 @@
|
||||
"parameters": {
|
||||
"description": "PostgreSQL server parameters",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"max_connections": 100
|
||||
},
|
||||
"required": [
|
||||
"max_connections"
|
||||
],
|
||||
@@ -138,7 +156,10 @@
|
||||
"quorum": {
|
||||
"description": "Quorum configuration for synchronous replication",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"maxSyncReplicas": 0,
|
||||
"minSyncReplicas": 0
|
||||
},
|
||||
"required": [
|
||||
"maxSyncReplicas",
|
||||
"minSyncReplicas"
|
||||
|
||||
@@ -16,7 +16,7 @@ The service utilizes official RabbitMQ operator. This ensures the reliability an
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of RabbitMQ replicas | `int` | `3` |
|
||||
| `resources` | Explicit CPU and memory configuration for each RabbitMQ replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each RabbitMQ replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
|
||||
|
||||
@@ -16,7 +16,7 @@ Service utilizes the Spotahome Redis Operator for efficient management and orche
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of Redis replicas | `int` | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each Redis replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each Redis replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
|
||||
|
||||
@@ -10,7 +10,14 @@
|
||||
"httpAndHttps": {
|
||||
"description": "HTTP and HTTPS configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"endpoints": {},
|
||||
"mode": "tcp",
|
||||
"targetPorts": {
|
||||
"http": 80,
|
||||
"https": 443
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"mode",
|
||||
"targetPorts"
|
||||
@@ -36,7 +43,10 @@
|
||||
"targetPorts": {
|
||||
"description": "Target ports configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"http": 80,
|
||||
"https": 443
|
||||
},
|
||||
"required": [
|
||||
"http",
|
||||
"https"
|
||||
|
||||
@@ -71,8 +71,7 @@ kubernetes 0.26.2 8ddbe32e
|
||||
kubernetes 0.26.3 c02a3818
|
||||
kubernetes 0.27.0 6cd5e746
|
||||
kubernetes 0.28.0 7f477eec
|
||||
kubernetes 0.29.0 87b23161
|
||||
kubernetes 0.29.1 HEAD
|
||||
kubernetes 0.29.0 HEAD
|
||||
mysql 0.1.0 263e47be
|
||||
mysql 0.2.0 c24a103f
|
||||
mysql 0.3.0 53f2365e
|
||||
|
||||
@@ -50,7 +50,7 @@ virtctl ssh <user>@<vm>
|
||||
| `systemDisk.storageClass` | StorageClass used to store the data | `*string` | `replicated` |
|
||||
| `gpus` | List of GPUs to attach | `[]object` | `[]` |
|
||||
| `gpus[i].name` | The name of the GPU to attach. This should match the GPU resource name in the cluster. | `string` | `""` |
|
||||
| `resources` | Resources | `*object` | `null` |
|
||||
| `resources` | Resources | `*object` | `{}` |
|
||||
| `resources.cpu` | The number of CPU cores allocated to the virtual machine | `*quantity` | `null` |
|
||||
| `resources.sockets` | The number of CPU sockets allocated to the virtual machine (used to define vCPU topology) | `*quantity` | `null` |
|
||||
| `resources.memory` | The amount of memory allocated to the virtual machine | `*quantity` | `null` |
|
||||
|
||||
@@ -168,7 +168,11 @@
|
||||
"systemDisk": {
|
||||
"description": "System disk configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"image": "ubuntu",
|
||||
"storage": "5Gi",
|
||||
"storageClass": "replicated"
|
||||
},
|
||||
"required": [
|
||||
"image",
|
||||
"storage"
|
||||
|
||||
@@ -49,7 +49,7 @@ virtctl ssh <user>@<vm>
|
||||
| `disks[i].bus` | Disk bus type, such as "sata" | `*string` | `null` |
|
||||
| `gpus` | List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM) | `[]object` | `[]` |
|
||||
| `gpus[i].name` | Name of GPU, such as "nvidia.com/AD102GL_L40S" | `string` | `""` |
|
||||
| `resources` | Resources | `*object` | `null` |
|
||||
| `resources` | Resources | `*object` | `{}` |
|
||||
| `resources.cpu` | The number of CPU cores allocated to the virtual machine | `*quantity` | `null` |
|
||||
| `resources.memory` | The amount of memory allocated to the virtual machine | `*quantity` | `null` |
|
||||
| `resources.sockets` | The number of CPU sockets allocated to the virtual machine (used to define vCPU topology) | `*quantity` | `null` |
|
||||
|
||||
@@ -22,7 +22,7 @@ Furthermore, Shadowbox is compatible with standard Shadowsocks clients, providin
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of VPN server replicas | `int` | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each VPN server replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each VPN server replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
cozystack:
|
||||
image: ghcr.io/cozystack/cozystack/installer:v0.36.1@sha256:1579855349bef729209e8668b96dbb03e0fc80b74bcae2c25f3ed5f3ae6d2f7f
|
||||
image: ghcr.io/cozystack/cozystack/installer:v0.36.0-beta.4@sha256:51cb9828af3bdceac289de3b1161625065db22535a961958530e9c751880ee96
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
e2e:
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.36.1@sha256:150efd626321c9389415da5779504be4f10e70beafaeb1b7c162b08b3d50b51f
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.36.0-beta.4@sha256:c70ad3321fc14ca831c84bf6e7e6e5409bfe8130703173c277ca51db740c6cb3
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/matchbox:v0.36.1@sha256:ecf30f70d9a4b708f68fab52ba3a2ecc0787bb2e79906d76b770bb51f8d6ad6c
|
||||
ghcr.io/cozystack/cozystack/matchbox:v0.36.0-beta.4@sha256:764c547a352c9c1d0442e43cdfd0ef50b216bd7f6e5514c777b1d90c4d95da92
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------- | ----------- | ------ |
|
||||
| `size` | Persistent Volume size | `*quantity` | `4Gi` |
|
||||
| `storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `replicas` | Number of etcd replicas | `*int` | `3` |
|
||||
| `resources` | Resource configuration for etcd | `*object` | `null` |
|
||||
| `resources.cpu` | The number of CPU cores allocated | `*quantity` | `4` |
|
||||
| `resources.memory` | The amount of memory allocated | `*quantity` | `1Gi` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------- | ----------- | ----- |
|
||||
| `size` | Persistent Volume size | `*quantity` | `4Gi` |
|
||||
| `storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `replicas` | Number of etcd replicas | `*int` | `3` |
|
||||
| `resources` | Resource configuration for etcd | `*object` | `{}` |
|
||||
| `resources.cpu` | The number of CPU cores allocated | `*quantity` | `4` |
|
||||
| `resources.memory` | The amount of memory allocated | `*quantity` | `1Gi` |
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
"resources": {
|
||||
"description": "Resource configuration for etcd",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"cpu": 4,
|
||||
"memory": "1Gi"
|
||||
},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "The number of CPU cores allocated",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
| `replicas` | Number of ingress-nginx replicas | `int` | `2` |
|
||||
| `whitelist` | List of client networks | `[]*string` | `[]` |
|
||||
| `cloudflareProxy` | Restoring original visitor IPs when Cloudflare proxied is enabled | `bool` | `false` |
|
||||
| `resources` | Explicit CPU and memory configuration for each ingress-nginx replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `resources` | Explicit CPU and memory configuration for each ingress-nginx replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
|
||||
@@ -3,4 +3,4 @@ name: monitoring
|
||||
description: Monitoring and observability stack
|
||||
icon: /logos/monitoring.svg
|
||||
type: application
|
||||
version: 1.13.1
|
||||
version: 1.13.0
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
| `metricsStorages[i].name` | Name of the storage instance | `string` | `""` |
|
||||
| `metricsStorages[i].retentionPeriod` | Retention period for the metrics in the storage instance | `string` | `""` |
|
||||
| `metricsStorages[i].deduplicationInterval` | Deduplication interval for the metrics in the storage instance | `string` | `""` |
|
||||
| `metricsStorages[i].storage` | Persistent Volume size for the storage instance | `string` | `10Gi` |
|
||||
| `metricsStorages[i].storage` | Persistent Volume size for the storage instance | `string` | `""` |
|
||||
| `metricsStorages[i].storageClassName` | StorageClass used to store the data | `*string` | `null` |
|
||||
| `metricsStorages[i].vminsert` | Configuration for vminsert component of the storage instance | `*object` | `null` |
|
||||
| `metricsStorages[i].vminsert.minAllowed` | Requests (minimum allowed/available resources) | `*object` | `null` |
|
||||
@@ -44,13 +44,13 @@
|
||||
|
||||
### Logs storage configuration
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------------------- | ----------------------------------------------------- | ---------- | ------------ |
|
||||
| `logsStorages` | Configuration of logs storage instances | `[]object` | `[...]` |
|
||||
| `logsStorages[i].name` | Name of the storage instance | `string` | `""` |
|
||||
| `logsStorages[i].retentionPeriod` | Retention period for the logs in the storage instance | `string` | `1` |
|
||||
| `logsStorages[i].storage` | Persistent Volume size for the storage instance | `string` | `10Gi` |
|
||||
| `logsStorages[i].storageClassName` | StorageClass used to store the data | `*string` | `replicated` |
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------------------- | ----------------------------------------------------- | ---------- | ------- |
|
||||
| `logsStorages` | Configuration of logs storage instances | `[]object` | `[...]` |
|
||||
| `logsStorages[i].name` | Name of the storage instance | `string` | `""` |
|
||||
| `logsStorages[i].retentionPeriod` | Retention period for the logs in the storage instance | `string` | `""` |
|
||||
| `logsStorages[i].storage` | Persistent Volume size for the storage instance | `string` | `""` |
|
||||
| `logsStorages[i].storageClassName` | StorageClass used to store the data | `*string` | `null` |
|
||||
|
||||
|
||||
### Alerta configuration
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/grafana:1.13.1@sha256:c63978e1ed0304e8518b31ddee56c4e8115541b997d8efbe1c0a74da57140399
|
||||
ghcr.io/cozystack/cozystack/grafana:1.13.0@sha256:c63978e1ed0304e8518b31ddee56c4e8115541b997d8efbe1c0a74da57140399
|
||||
|
||||
@@ -5,17 +5,47 @@
|
||||
"alerta": {
|
||||
"description": "Configuration for Alerta service",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"alerts": {
|
||||
"telegram": {
|
||||
"chatID": "",
|
||||
"disabledSeverity": "",
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"limits": {
|
||||
"cpu": "1",
|
||||
"memory": "1Gi"
|
||||
},
|
||||
"requests": {
|
||||
"cpu": "100m",
|
||||
"memory": "256Mi"
|
||||
}
|
||||
},
|
||||
"storage": "10Gi",
|
||||
"storageClassName": ""
|
||||
},
|
||||
"properties": {
|
||||
"alerts": {
|
||||
"description": "Configuration for alerts",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"telegram": {
|
||||
"chatID": "",
|
||||
"disabledSeverity": "",
|
||||
"token": ""
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"telegram": {
|
||||
"description": "Configuration for Telegram alerts",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"chatID": "",
|
||||
"disabledSeverity": "",
|
||||
"token": ""
|
||||
},
|
||||
"required": [
|
||||
"chatID",
|
||||
"disabledSeverity",
|
||||
@@ -41,11 +71,23 @@
|
||||
"resources": {
|
||||
"description": "Resources configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"limits": {
|
||||
"cpu": "1",
|
||||
"memory": "1Gi"
|
||||
},
|
||||
"requests": {
|
||||
"cpu": "100m",
|
||||
"memory": "256Mi"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"limits": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"cpu": "1",
|
||||
"memory": "1Gi"
|
||||
},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU limit (maximum available CPU)",
|
||||
@@ -79,7 +121,10 @@
|
||||
},
|
||||
"requests": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"cpu": "100m",
|
||||
"memory": "256Mi"
|
||||
},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU request (minimum available CPU)",
|
||||
@@ -127,12 +172,28 @@
|
||||
"grafana": {
|
||||
"description": "Configuration for Grafana",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"db": {
|
||||
"size": "10Gi"
|
||||
},
|
||||
"resources": {
|
||||
"limits": {
|
||||
"cpu": "1",
|
||||
"memory": "1Gi"
|
||||
},
|
||||
"requests": {
|
||||
"cpu": "100m",
|
||||
"memory": "256Mi"
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"db": {
|
||||
"description": "Database configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"size": "10Gi"
|
||||
},
|
||||
"properties": {
|
||||
"size": {
|
||||
"description": "Persistent Volume size for the database",
|
||||
@@ -144,11 +205,23 @@
|
||||
"resources": {
|
||||
"description": "Resources configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"limits": {
|
||||
"cpu": "1",
|
||||
"memory": "1Gi"
|
||||
},
|
||||
"requests": {
|
||||
"cpu": "100m",
|
||||
"memory": "256Mi"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"limits": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"cpu": "1",
|
||||
"memory": "1Gi"
|
||||
},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU limit (maximum available CPU)",
|
||||
@@ -182,7 +255,10 @@
|
||||
},
|
||||
"requests": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"cpu": "100m",
|
||||
"memory": "256Mi"
|
||||
},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU request (minimum available CPU)",
|
||||
@@ -247,18 +323,15 @@
|
||||
},
|
||||
"retentionPeriod": {
|
||||
"description": "Retention period for the logs in the storage instance",
|
||||
"type": "string",
|
||||
"default": "1"
|
||||
"type": "string"
|
||||
},
|
||||
"storage": {
|
||||
"description": "Persistent Volume size for the storage instance",
|
||||
"type": "string",
|
||||
"default": "10Gi"
|
||||
"type": "string"
|
||||
},
|
||||
"storageClassName": {
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string",
|
||||
"default": "replicated"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,8 +378,7 @@
|
||||
},
|
||||
"storage": {
|
||||
"description": "Persistent Volume size for the storage instance",
|
||||
"type": "string",
|
||||
"default": "10Gi"
|
||||
"type": "string"
|
||||
},
|
||||
"storageClassName": {
|
||||
"description": "StorageClass used to store the data",
|
||||
|
||||
@@ -9,7 +9,7 @@ host: ""
|
||||
## @field metricsStorage.name {string} Name of the storage instance
|
||||
## @field metricsStorage.retentionPeriod {string} Retention period for the metrics in the storage instance
|
||||
## @field metricsStorage.deduplicationInterval {string} Deduplication interval for the metrics in the storage instance
|
||||
## @field metricsStorage.storage {string default="10Gi"} Persistent Volume size for the storage instance
|
||||
## @field metricsStorage.storage {string} Persistent Volume size for the storage instance
|
||||
## @field metricsStorage.storageClassName {*string} StorageClass used to store the data
|
||||
## @field metricsStorage.vminsert {*vmcomponent} Configuration for vminsert component of the storage instance
|
||||
## @field metricsStorage.vmselect {*vmcomponent} Configuration for vmselect component of the storage instance
|
||||
@@ -69,9 +69,9 @@ metricsStorages:
|
||||
|
||||
## @param logsStorages {[]logsStorage} Configuration of logs storage instances
|
||||
## @field logsStorage.name {string} Name of the storage instance
|
||||
## @field logsStorage.retentionPeriod {string default=1} Retention period for the logs in the storage instance
|
||||
## @field logsStorage.storage {string default="10Gi"} Persistent Volume size for the storage instance
|
||||
## @field logsStorage.storageClassName {*string default="replicated"} StorageClass used to store the data
|
||||
## @field logsStorage.retentionPeriod {string} Retention period for the logs in the storage instance
|
||||
## @field logsStorage.storage {string} Persistent Volume size for the storage instance
|
||||
## @field logsStorage.storageClassName {*string} StorageClass used to store the data
|
||||
##
|
||||
logsStorages:
|
||||
- name: generic
|
||||
|
||||
@@ -13,46 +13,46 @@
|
||||
|
||||
### SeaweedFS Components Configuration
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| `db` | Database Configuration | `object` | `{}` |
|
||||
| `db.replicas` | Number of database replicas | `*int` | `2` |
|
||||
| `db.size` | Persistent Volume size | `*quantity` | `10Gi` |
|
||||
| `db.storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `db.resources` | Explicit CPU and memory configuration for the database. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `db.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `db.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `db.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `master` | Master service configuration | `*object` | `null` |
|
||||
| `master.replicas` | Number of master replicas | `*int` | `3` |
|
||||
| `master.resources` | Explicit CPU and memory configuration for the master. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `master.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `master.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `master.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `filer` | Filer service configuration | `*object` | `null` |
|
||||
| `filer.replicas` | Number of filer replicas | `*int` | `2` |
|
||||
| `filer.resources` | Explicit CPU and memory configuration for the filer. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `filer.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `filer.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `filer.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `filer.grpcHost` | The hostname used to expose or access the filer service externally. | `*string` | `""` |
|
||||
| `filer.grpcPort` | The port used to access the filer service externally. | `*int` | `443` |
|
||||
| `filer.whitelist` | A list of IP addresses or CIDR ranges that are allowed to access the filer service. | `[]*string` | `[]` |
|
||||
| `volume` | Volume service configuration | `*object` | `null` |
|
||||
| `volume.replicas` | Number of volume replicas | `*int` | `2` |
|
||||
| `volume.size` | Persistent Volume size | `*quantity` | `10Gi` |
|
||||
| `volume.storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `volume.resources` | Explicit CPU and memory configuration for the volume. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `volume.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `volume.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `volume.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `volume.zones` | A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. | `map[string]object` | `{}` |
|
||||
| `volume.zones[name].replicas` | Number of replicas in the zone | `*int` | `null` |
|
||||
| `volume.zones[name].size` | Zone storage size | `*quantity` | `null` |
|
||||
| `s3` | S3 service configuration | `*object` | `null` |
|
||||
| `s3.replicas` | Number of s3 replicas | `*int` | `2` |
|
||||
| `s3.resources` | Explicit CPU and memory configuration for the s3. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `s3.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `s3.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `s3.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| `db` | Database Configuration | `object` | `{}` |
|
||||
| `db.replicas` | Number of database replicas | `*int` | `2` |
|
||||
| `db.size` | Persistent Volume size | `*quantity` | `10Gi` |
|
||||
| `db.storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `db.resources` | Explicit CPU and memory configuration for the database. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `db.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `db.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `db.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `master` | Master service configuration | `*object` | `{}` |
|
||||
| `master.replicas` | Number of master replicas | `*int` | `3` |
|
||||
| `master.resources` | Explicit CPU and memory configuration for the master. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `master.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `master.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `master.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `filer` | Filer service configuration | `*object` | `{}` |
|
||||
| `filer.replicas` | Number of filer replicas | `*int` | `2` |
|
||||
| `filer.resources` | Explicit CPU and memory configuration for the filer. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `filer.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `filer.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `filer.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `filer.grpcHost` | The hostname used to expose or access the filer service externally. | `*string` | `""` |
|
||||
| `filer.grpcPort` | The port used to access the filer service externally. | `*int` | `443` |
|
||||
| `filer.whitelist` | A list of IP addresses or CIDR ranges that are allowed to access the filer service. | `[]*string` | `[]` |
|
||||
| `volume` | Volume service configuration | `*object` | `{}` |
|
||||
| `volume.replicas` | Number of volume replicas | `*int` | `2` |
|
||||
| `volume.size` | Persistent Volume size | `*quantity` | `10Gi` |
|
||||
| `volume.storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `volume.resources` | Explicit CPU and memory configuration for the volume. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `volume.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `volume.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `volume.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `volume.zones` | A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. | `map[string]object` | `{}` |
|
||||
| `volume.zones.replicas` | Number of replicas in the zone | `*int` | `null` |
|
||||
| `volume.zones.size` | Zone storage size | `*quantity` | `null` |
|
||||
| `s3` | S3 service configuration | `*object` | `{}` |
|
||||
| `s3.replicas` | Number of s3 replicas | `*int` | `2` |
|
||||
| `s3.resources` | Explicit CPU and memory configuration for the s3. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `s3.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `s3.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `s3.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.36.1@sha256:890c6f38d22fa8cba423d086686bd55c20b3d0c27881cf4b7a7801f1b0685112
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.36.0-beta.4@sha256:24451989b15b6801b33ad355a5507307d0333bf9afd240f1db0aca9c92f6b2ad
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
"db": {
|
||||
"description": "Database Configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"size": "10Gi",
|
||||
"storageClass": ""
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
@@ -86,7 +92,14 @@
|
||||
"filer": {
|
||||
"description": "Filer service configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"grpcHost": "",
|
||||
"grpcPort": 443,
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"whitelist": {}
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
@@ -170,7 +183,11 @@
|
||||
"master": {
|
||||
"description": "Master service configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"replicas": 3,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
@@ -238,7 +255,11 @@
|
||||
"s3": {
|
||||
"description": "S3 service configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
@@ -311,7 +332,14 @@
|
||||
"volume": {
|
||||
"description": "Volume service configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"size": "10Gi",
|
||||
"storageClass": "",
|
||||
"zones": {}
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
|
||||
@@ -56,8 +56,7 @@ monitoring 1.10.1 8c86905b
|
||||
monitoring 1.11.0 4369b031
|
||||
monitoring 1.12.0 0e47e1e8
|
||||
monitoring 1.12.1 c02a3818
|
||||
monitoring 1.13.0 87b23161
|
||||
monitoring 1.13.1 HEAD
|
||||
monitoring 1.13.0 HEAD
|
||||
seaweedfs 0.1.0 71514249
|
||||
seaweedfs 0.2.0 5fb9cfe3
|
||||
seaweedfs 0.2.1 fde4bcfa
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:899ea667b3e575244d512cade23f30cc93d768b070f9c2bebcb440e443444bdb
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:b9a401defb90a822087e50e7ab6afd9b4db7e71728030f92c7d320ac46889053
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
cozystackAPI:
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.36.1@sha256:a9ce8848b0a46e52ce47ad10fcccc4e848740f5cf1d4e6cb78fc4a196e167e1b
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.36.0-beta.4@sha256:c1d6534b36a24f365d64383fd3deff469a71565200ae1789eabf78e3cd9a3601
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: cozystack-controller-webhook-selfsigned
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
selfSigned: {}
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: cozystack-controller-webhook-ca
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
secretName: cozystack-controller-webhook-ca
|
||||
duration: 43800h # 5 years
|
||||
commonName: cozystack-controller-webhook-ca
|
||||
issuerRef:
|
||||
name: cozystack-controller-webhook-selfsigned
|
||||
isCA: true
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: cozystack-controller-webhook-ca
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
ca:
|
||||
secretName: cozystack-controller-webhook-ca
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: cozystack-controller-webhook
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
secretName: cozystack-controller-webhook-cert
|
||||
duration: 8760h
|
||||
renewBefore: 720h
|
||||
issuerRef:
|
||||
name: cozystack-controller-webhook-ca
|
||||
commonName: cozystack-controller
|
||||
dnsNames:
|
||||
- cozystack-controller
|
||||
- cozystack-controller.{{ .Release.Namespace }}.svc
|
||||
@@ -2,7 +2,6 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cozystack-controller
|
||||
namespace: cozy-system
|
||||
labels:
|
||||
app: cozystack-controller
|
||||
spec:
|
||||
@@ -29,3 +28,15 @@ spec:
|
||||
{{- if .Values.cozystackController.disableTelemetry }}
|
||||
- --disable-telemetry
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: webhook
|
||||
containerPort: 9443
|
||||
volumeMounts:
|
||||
- name: webhook-certs
|
||||
mountPath: /tmp/k8s-webhook-server/serving-certs
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: webhook-certs
|
||||
secret:
|
||||
secretName: cozystack-controller-webhook-cert
|
||||
defaultMode: 0400
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
name: lineage
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/cozystack-controller-webhook
|
||||
labels:
|
||||
app: cozystack-controller
|
||||
webhooks:
|
||||
- name: lineage.cozystack.io
|
||||
admissionReviewVersions: ["v1"]
|
||||
sideEffects: None
|
||||
clientConfig:
|
||||
service:
|
||||
name: cozystack-controller
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /mutate-lineage
|
||||
rules:
|
||||
- operations: ["CREATE"]
|
||||
apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
resources: ["pods","secrets", "services", "persistentvolumeclaims"]
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: cozystack.io/system
|
||||
operator: NotIn
|
||||
values:
|
||||
- "true"
|
||||
- key: kubernetes.io/metadata.name
|
||||
operator: NotIn
|
||||
values:
|
||||
- kube-system
|
||||
15
packages/system/cozystack-controller/templates/service.yaml
Normal file
15
packages/system/cozystack-controller/templates/service.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cozystack-controller
|
||||
labels:
|
||||
app: cozystack-controller
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 443
|
||||
targetPort: 9443
|
||||
protocol: TCP
|
||||
name: webhook
|
||||
selector:
|
||||
app: cozystack-controller
|
||||
@@ -1,5 +1,5 @@
|
||||
cozystackController:
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.36.1@sha256:6a6144430bdec901b4046840a484f0d9effc570f4f7cef2e5740332b98e3077a
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.36.0-beta.4@sha256:f4f8fa8e2f33f66d90b99398025d0da1328b530a622a9b420c993e51e1302992
|
||||
debug: false
|
||||
disableTelemetry: false
|
||||
cozystackVersion: "v0.36.1"
|
||||
cozystackVersion: "v0.36.0-beta.4"
|
||||
|
||||
@@ -76,7 +76,7 @@ data:
|
||||
"kubeappsNamespace": {{ .Release.Namespace | quote }},
|
||||
"helmGlobalNamespace": {{ include "kubeapps.helmGlobalPackagingNamespace" . | quote }},
|
||||
"carvelGlobalNamespace": {{ .Values.kubeappsapis.pluginConfig.kappController.packages.v1alpha1.globalPackagingNamespace | quote }},
|
||||
"appVersion": "v0.36.1",
|
||||
"appVersion": "v0.36.0-beta.4",
|
||||
"authProxyEnabled": {{ .Values.authProxy.enabled }},
|
||||
"oauthLoginURI": {{ .Values.authProxy.oauthLoginURI | quote }},
|
||||
"oauthLogoutURI": {{ .Values.authProxy.oauthLogoutURI | quote }},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM bitnamilegacy/node:20.15.1 AS build
|
||||
FROM bitnami/node:20.15.1 AS build
|
||||
WORKDIR /app
|
||||
|
||||
ARG COMMIT_REF=4926bc68fabb0914afab574006643c85a597b371
|
||||
@@ -26,5 +26,5 @@ RUN sed -i \
|
||||
-e 's/#1b2b32/#2a2d2f/g' \
|
||||
$(grep -rl "#2d4048\|#25333d\|#fcfdfd\|#f1f6f8\|#e3eaed\|#cbd4d8\|#aeb8bc\|#859399\|#6a7a81\|#4f6169\|#3a4d55\|#2d4048\|#21333b\|#1b2b32")
|
||||
|
||||
FROM bitnamilegacy/nginx:1.25.2
|
||||
FROM bitnami/nginx:1.25.2
|
||||
COPY --from=build /app/build /app
|
||||
|
||||
@@ -9,7 +9,7 @@ RUN apk add --no-cache patch
|
||||
WORKDIR /source
|
||||
RUN wget -O- https://github.com/cozystack/kubeapps/archive/${COMMIT_REF}.tar.gz | tar xzf - --strip-components=1
|
||||
|
||||
FROM bitnamilegacy/golang:1.23.4 AS builder
|
||||
FROM bitnami/golang:1.23.4 AS builder
|
||||
WORKDIR /go/src/github.com/vmware-tanzu/kubeapps
|
||||
COPY --from=source /source/go.mod /source/go.sum ./
|
||||
ARG TARGETOS
|
||||
|
||||
@@ -19,7 +19,7 @@ kubeapps:
|
||||
image:
|
||||
registry: ghcr.io/cozystack/cozystack
|
||||
repository: dashboard
|
||||
tag: v0.36.1
|
||||
tag: v0.36.0-beta.4
|
||||
digest: "sha256:54906b3d2492c8603a347a5938b6db36e5ed5c4149111cae1804ac9110361947"
|
||||
frontend:
|
||||
image:
|
||||
@@ -48,8 +48,8 @@ kubeapps:
|
||||
image:
|
||||
registry: ghcr.io/cozystack/cozystack
|
||||
repository: kubeapps-apis
|
||||
tag: v0.36.1
|
||||
digest: "sha256:95cec1059acc1e307f9346bdf570cfc1ca5962bdb803ff08ccd17712d1d8f485"
|
||||
tag: v0.36.0-beta.4
|
||||
digest: "sha256:c4b268996c96d23bc11b6d109fb7fb51faf05576ee326097889eca72c851b656"
|
||||
pluginConfig:
|
||||
flux:
|
||||
packages:
|
||||
|
||||
@@ -3,7 +3,7 @@ kamaji:
|
||||
deploy: false
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
tag: v0.36.1@sha256:61bd1a046d0ad2bc90bf4507aa41073989001d13606e9b8cf8f318edcfadf2c7
|
||||
tag: v0.36.0-beta.4@sha256:bcf8ccde72e5f8619b626a290c1e4c81018d9a10497e66948afebabd80a64023
|
||||
repository: ghcr.io/cozystack/cozystack/kamaji
|
||||
resources:
|
||||
limits:
|
||||
@@ -13,4 +13,4 @@ kamaji:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
extraArgs:
|
||||
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.36.1@sha256:61bd1a046d0ad2bc90bf4507aa41073989001d13606e9b8cf8f318edcfadf2c7
|
||||
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.36.0-beta.4@sha256:bcf8ccde72e5f8619b626a290c1e4c81018d9a10497e66948afebabd80a64023
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
portSecurity: true
|
||||
routes: ""
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.36.1@sha256:b38c0610e9648d21326be30a773173757897d2ba9ec438272c8ae3d738956b66
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.36.0-beta.4@sha256:fb334a9cf9b6fa606b2530cb4227e0de303761151ff93aa52209dcbcf8b33ef8
|
||||
ovnCentralName: ovn-central
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
portSecurity: true
|
||||
routes: ""
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.36.1@sha256:8fa755cdb024c7bdeff390d01d6b8569d60a4b244a7371209be4c4851df5fad4
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.36.0-beta.4@sha256:583922648d9e39f4e4d2255a50f5db74b1aa2b1590982e891d0ea6d6cb9ec453
|
||||
|
||||
@@ -64,4 +64,4 @@ global:
|
||||
images:
|
||||
kubeovn:
|
||||
repository: kubeovn
|
||||
tag: v1.14.5@sha256:7035b06406493c3385645319a50f2198c6bfb343d2e8c3f0707e769db1e960f7
|
||||
tag: v1.14.5@sha256:8968977ba60e1fb14121984899ddf38f7fe4ea800806a7a30db007110064c84b
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
storageClass: replicated
|
||||
csiDriver:
|
||||
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.29.1@sha256:cae43eae09fc39e5f2140d30ef55253f871cc565b8b7a564a54077b7cbd92212
|
||||
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.29.0@sha256:3a3bc912f70ccba1e9f92a0754179dbdc4c01f24073467b6d1406c77da794863
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
objectstorage:
|
||||
controller:
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.36.1@sha256:aa0000265ae58155aebefedac72d0a6acc45437b8668bb9739bf11edefec067a"
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.36.0-beta.4@sha256:aa0000265ae58155aebefedac72d0a6acc45437b8668bb9739bf11edefec067a"
|
||||
|
||||
@@ -118,7 +118,7 @@ seaweedfs:
|
||||
bucketClassName: "seaweedfs"
|
||||
region: ""
|
||||
sidecar:
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.36.1@sha256:890c6f38d22fa8cba423d086686bd55c20b3d0c27881cf4b7a7801f1b0685112"
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.36.0-beta.4@sha256:24451989b15b6801b33ad355a5507307d0333bf9afd240f1db0aca9c92f6b2ad"
|
||||
certificates:
|
||||
commonName: "SeaweedFS CA"
|
||||
ipAddresses: []
|
||||
|
||||
193
pkg/lineage/lineage.go
Normal file
193
pkg/lineage/lineage.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package lineage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
const (
|
||||
HRAPIVersion = "helm.toolkit.fluxcd.io/v2"
|
||||
HRKind = "HelmRelease"
|
||||
HRLabel = "helm.toolkit.fluxcd.io/name"
|
||||
)
|
||||
|
||||
type ObjectID struct {
|
||||
APIVersion string
|
||||
Kind string
|
||||
Namespace string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (o ObjectID) GetUnstructured(ctx context.Context, client dynamic.Interface, mapper meta.RESTMapper) (*unstructured.Unstructured, error) {
|
||||
u, err := getUnstructuredObject(ctx, client, mapper, o.APIVersion, o.Kind, o.Namespace, o.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func WalkOwnershipGraph(
|
||||
ctx context.Context,
|
||||
client dynamic.Interface,
|
||||
mapper meta.RESTMapper,
|
||||
appMapper AppMapper,
|
||||
obj *unstructured.Unstructured,
|
||||
memory ...interface{},
|
||||
) (out []ObjectID) {
|
||||
|
||||
id := ObjectID{APIVersion: obj.GetAPIVersion(), Kind: obj.GetKind(), Namespace: obj.GetNamespace(), Name: obj.GetName()}
|
||||
out = []ObjectID{}
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
l.Info("processing object", "apiVersion", obj.GetAPIVersion(), "kind", obj.GetKind(), "name", obj.GetName())
|
||||
var visited map[ObjectID]bool
|
||||
var ok bool
|
||||
if len(memory) == 1 {
|
||||
visited, ok = memory[0].(map[ObjectID]bool)
|
||||
if !ok {
|
||||
l.Error(
|
||||
fmt.Errorf("invalid argument"), "could not parse visited map in WalkOwnershipGraph call",
|
||||
"received", memory[0], "expected", "map[ObjectID]bool",
|
||||
)
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
if len(memory) == 0 {
|
||||
visited = make(map[ObjectID]bool)
|
||||
}
|
||||
|
||||
if len(memory) != 0 && len(memory) != 1 {
|
||||
l.Error(
|
||||
fmt.Errorf("invalid argument count"), "could not parse variadic arguments to WalkOwnershipGraph",
|
||||
"args passed", len(memory)+5, "expected args", "4|5",
|
||||
)
|
||||
return out
|
||||
}
|
||||
|
||||
if visited[id] {
|
||||
return out
|
||||
}
|
||||
|
||||
visited[id] = true
|
||||
|
||||
ownerRefs := obj.GetOwnerReferences()
|
||||
for _, owner := range ownerRefs {
|
||||
ownerObj, err := getUnstructuredObject(ctx, client, mapper, owner.APIVersion, owner.Kind, obj.GetNamespace(), owner.Name)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Could not fetch owner %s/%s (%s): %v\n", obj.GetNamespace(), owner.Name, owner.Kind, err)
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, WalkOwnershipGraph(ctx, client, mapper, appMapper, ownerObj, visited)...)
|
||||
}
|
||||
|
||||
// if object has owners, it couldn't be owned directly by the custom app
|
||||
if len(ownerRefs) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// I want "if err1 != nil go to next block, if err2 != nil, go to next block, etc semantics",
|
||||
// like an early return from a function, but if all checks succeed, I don't want to do the rest
|
||||
// of the function, so it's a `for { if err { break } if othererr { break } if allgood { return }
|
||||
for {
|
||||
if obj.GetAPIVersion() != HRAPIVersion || obj.GetKind() != HRKind {
|
||||
break
|
||||
}
|
||||
hr := helmReleaseFromUnstructured(obj)
|
||||
if hr == nil {
|
||||
break
|
||||
}
|
||||
a, k, p, err := appMapper.Map(hr)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
ownerObj, err := getUnstructuredObject(ctx, client, mapper, a, k, obj.GetNamespace(), strings.TrimPrefix(obj.GetName(), p))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
// successfully mapped a HelmRelease to a custom app, no need to continue
|
||||
out = append(out,
|
||||
ObjectID{
|
||||
APIVersion: ownerObj.GetAPIVersion(),
|
||||
Kind: ownerObj.GetKind(),
|
||||
Namespace: ownerObj.GetNamespace(),
|
||||
Name: ownerObj.GetName(),
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
labels := obj.GetLabels()
|
||||
name, ok := labels[HRLabel]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ownerObj, err := getUnstructuredObject(ctx, client, mapper, HRAPIVersion, HRKind, obj.GetNamespace(), name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out = append(out, WalkOwnershipGraph(ctx, client, mapper, appMapper, ownerObj, visited)...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getUnstructuredObject(
|
||||
ctx context.Context,
|
||||
client dynamic.Interface,
|
||||
mapper meta.RESTMapper,
|
||||
apiVersion, kind, namespace, name string,
|
||||
) (*unstructured.Unstructured, error) {
|
||||
l := log.FromContext(ctx)
|
||||
gv, err := schema.ParseGroupVersion(apiVersion)
|
||||
if err != nil {
|
||||
l.Error(
|
||||
err, "failed to parse groupversion",
|
||||
"apiVersion", apiVersion,
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: gv.Group,
|
||||
Version: gv.Version,
|
||||
Kind: kind,
|
||||
}
|
||||
|
||||
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
l.Error(err, "Could not map GVK "+gvk.String())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ns := namespace
|
||||
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
|
||||
ns = ""
|
||||
}
|
||||
|
||||
ownerObj, err := client.Resource(mapping.Resource).Namespace(ns).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ownerObj, nil
|
||||
}
|
||||
|
||||
func helmReleaseFromUnstructured(obj *unstructured.Unstructured) *helmv2.HelmRelease {
|
||||
if obj.GetAPIVersion() == HRAPIVersion && obj.GetKind() == HRKind {
|
||||
hr := &helmv2.HelmRelease{}
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, hr); err == nil {
|
||||
return hr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
53
pkg/lineage/lineage_test.go
Normal file
53
pkg/lineage/lineage_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package lineage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/go-logr/zapr"
|
||||
"go.uber.org/zap"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/discovery/cached/memory"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
var (
|
||||
dynClient dynamic.Interface
|
||||
mapper meta.RESTMapper
|
||||
l logr.Logger
|
||||
ctx context.Context
|
||||
)
|
||||
|
||||
func init() {
|
||||
cfg := config.GetConfigOrDie()
|
||||
|
||||
dynClient, _ = dynamic.NewForConfig(cfg)
|
||||
|
||||
discoClient, _ := discovery.NewDiscoveryClientForConfig(cfg)
|
||||
|
||||
cachedDisco := memory.NewMemCacheClient(discoClient)
|
||||
mapper = restmapper.NewDeferredDiscoveryRESTMapper(cachedDisco)
|
||||
|
||||
zapLogger, _ := zap.NewDevelopment()
|
||||
l = zapr.NewLogger(zapLogger)
|
||||
ctx = logr.NewContext(context.Background(), l)
|
||||
}
|
||||
|
||||
func TestWalkingOwnershipGraph(t *testing.T) {
|
||||
obj, err := dynClient.Resource(schema.GroupVersionResource{"", "v1", "pods"}).Namespace(os.Args[1]).Get(ctx, os.Args[2], metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nodes := WalkOwnershipGraph(ctx, dynClient, mapper, obj)
|
||||
for _, node := range nodes {
|
||||
fmt.Printf("%#v\n", node)
|
||||
}
|
||||
}
|
||||
49
pkg/lineage/mapper.go
Normal file
49
pkg/lineage/mapper.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package lineage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
)
|
||||
|
||||
type AppMapper interface {
|
||||
Map(*helmv2.HelmRelease) (apiVersion, kind, prefix string, err error)
|
||||
}
|
||||
|
||||
type stubMapper struct{}
|
||||
|
||||
var stubMapperMap = map[string]string{
|
||||
"cozystack-extra/bootbox": "apps.cozystack.io/v1alpha1/BootBox/",
|
||||
"cozystack-apps/bucket": "apps.cozystack.io/v1alpha1/Bucket/bucket-",
|
||||
"cozystack-apps/clickhouse": "apps.cozystack.io/v1alpha1/ClickHouse/clickhouse-",
|
||||
"cozystack-extra/etcd": "apps.cozystack.io/v1alpha1/Etcd/",
|
||||
"cozystack-apps/ferretdb": "apps.cozystack.io/v1alpha1/FerretDB/ferretdb-",
|
||||
"cozystack-apps/http-cache": "apps.cozystack.io/v1alpha1/HTTPCache/http-cache-",
|
||||
"cozystack-extra/info": "apps.cozystack.io/v1alpha1/Info/",
|
||||
"cozystack-extra/ingress": "apps.cozystack.io/v1alpha1/Ingress/",
|
||||
"cozystack-apps/kafka": "apps.cozystack.io/v1alpha1/Kafka/kafka-",
|
||||
"cozystack-apps/kubernetes": "apps.cozystack.io/v1alpha1/Kubernetes/kubernetes-",
|
||||
"cozystack-extra/monitoring": "apps.cozystack.io/v1alpha1/Monitoring/",
|
||||
"cozystack-apps/mysql": "apps.cozystack.io/v1alpha1/MySQL/mysql-",
|
||||
"cozystack-apps/nats": "apps.cozystack.io/v1alpha1/NATS/nats-",
|
||||
"cozystack-apps/postgres": "apps.cozystack.io/v1alpha1/Postgres/postgres-",
|
||||
"cozystack-apps/rabbitmq": "apps.cozystack.io/v1alpha1/RabbitMQ/rabbitmq-",
|
||||
"cozystack-apps/redis": "apps.cozystack.io/v1alpha1/Redis/redis-",
|
||||
"cozystack-extra/seaweedfs": "apps.cozystack.io/v1alpha1/SeaweedFS/",
|
||||
"cozystack-apps/tcp-balancer": "apps.cozystack.io/v1alpha1/TCPBalancer/tcp-balancer-",
|
||||
"cozystack-apps/tenant": "apps.cozystack.io/v1alpha1/Tenant/tenant-",
|
||||
"cozystack-apps/virtual-machine": "apps.cozystack.io/v1alpha1/VirtualMachine/virtual-machine-",
|
||||
"cozystack-apps/vm-disk": "apps.cozystack.io/v1alpha1/VMDisk/vm-disk-",
|
||||
"cozystack-apps/vm-instance": "apps.cozystack.io/v1alpha1/VMInstance/vm-instance-",
|
||||
"cozystack-apps/vpn": "apps.cozystack.io/v1alpha1/VPN/vpn-",
|
||||
}
|
||||
|
||||
func (s *stubMapper) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
|
||||
val, ok := stubMapperMap[hr.Spec.Chart.Spec.SourceRef.Name+"/"+hr.Spec.Chart.Spec.Chart]
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
|
||||
}
|
||||
split := strings.Split(val, "/")
|
||||
return strings.Join(split[:2], "/"), split[2], split[3], nil
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
internalapiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
schemadefault "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting"
|
||||
|
||||
// Importing API errors package to construct appropriate error responses
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -88,24 +89,12 @@ type REST struct {
|
||||
// NewREST creates a new REST storage for Application with specific configuration
|
||||
func NewREST(dynamicClient dynamic.Interface, config *config.Resource) *REST {
|
||||
var specSchema *structuralschema.Structural
|
||||
|
||||
if raw := strings.TrimSpace(config.Application.OpenAPISchema); raw != "" {
|
||||
var v1js apiextv1.JSONSchemaProps
|
||||
if err := json.Unmarshal([]byte(raw), &v1js); err != nil {
|
||||
klog.Errorf("Failed to unmarshal v1 OpenAPI schema: %v", err)
|
||||
} else {
|
||||
scheme := runtime.NewScheme()
|
||||
_ = internalapiext.AddToScheme(scheme)
|
||||
_ = apiextv1.AddToScheme(scheme)
|
||||
|
||||
var ijs internalapiext.JSONSchemaProps
|
||||
if err := scheme.Convert(&v1js, &ijs, nil); err != nil {
|
||||
klog.Errorf("Failed to convert v1->internal JSONSchemaProps: %v", err)
|
||||
} else if s, err := structuralschema.NewStructural(&ijs); err != nil {
|
||||
klog.Errorf("Failed to create structural schema: %v", err)
|
||||
} else {
|
||||
specSchema = s
|
||||
}
|
||||
var js internalapiext.JSONSchemaProps
|
||||
if err := json.Unmarshal([]byte(raw), &js); err != nil {
|
||||
klog.Errorf("Failed to unmarshal OpenAPI schema: %v", err)
|
||||
} else if specSchema, err = structuralschema.NewStructural(&js); err != nil {
|
||||
klog.Errorf("Failed to create structural schema: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1216,3 +1205,28 @@ func (e errNotAcceptable) Status() metav1.Status {
|
||||
Message: e.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// applySpecDefaults applies default values to the Application spec based on the schema
|
||||
func (r *REST) applySpecDefaults(app *appsv1alpha1.Application) error {
|
||||
if r.specSchema == nil {
|
||||
return nil
|
||||
}
|
||||
var m map[string]any
|
||||
if app.Spec != nil && len(app.Spec.Raw) > 0 {
|
||||
if err := json.Unmarshal(app.Spec.Raw, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if m == nil {
|
||||
m = map[string]any{}
|
||||
}
|
||||
|
||||
schemadefault.Default(m, r.specSchema)
|
||||
|
||||
raw, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app.Spec = &apiextv1.JSON{Raw: raw}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 The Cozystack Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package application
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
)
|
||||
|
||||
// applySpecDefaults applies default values to the Application spec based on the schema
|
||||
func (r *REST) applySpecDefaults(app *appsv1alpha1.Application) error {
|
||||
if r.specSchema == nil {
|
||||
return nil
|
||||
}
|
||||
var m map[string]any
|
||||
if app.Spec != nil && len(app.Spec.Raw) > 0 {
|
||||
if err := json.Unmarshal(app.Spec.Raw, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if m == nil {
|
||||
m = map[string]any{}
|
||||
}
|
||||
if err := defaultLikeKubernetes(&m, r.specSchema); err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app.Spec = &apiextv1.JSON{Raw: raw}
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultLikeKubernetes(root *map[string]any, s *structuralschema.Structural) error {
|
||||
v := any(*root)
|
||||
nv, err := applyDefaults(v, s, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj, ok := nv.(map[string]any)
|
||||
if !ok && nv != nil {
|
||||
return fmt.Errorf("internal error: applyDefaults returned non-map type %T for object root", nv)
|
||||
}
|
||||
if obj == nil {
|
||||
obj = map[string]any{}
|
||||
}
|
||||
*root = obj
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyDefaults(v any, s *structuralschema.Structural, top bool) (any, error) {
|
||||
if s == nil {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
effType := s.Generic.Type
|
||||
if effType == "" {
|
||||
switch {
|
||||
case len(s.Properties) > 0 || (s.AdditionalProperties != nil && s.AdditionalProperties.Structural != nil):
|
||||
effType = "object"
|
||||
case s.Items != nil:
|
||||
effType = "array"
|
||||
default:
|
||||
// scalar
|
||||
}
|
||||
}
|
||||
|
||||
switch effType {
|
||||
case "object":
|
||||
mv, isMap := v.(map[string]any)
|
||||
if !isMap || v == nil {
|
||||
if s.Generic.Default.Object != nil && !top {
|
||||
if dm, ok := s.Generic.Default.Object.(map[string]any); ok {
|
||||
mv = cloneMap(dm)
|
||||
}
|
||||
}
|
||||
if mv == nil {
|
||||
mv = map[string]any{}
|
||||
}
|
||||
}
|
||||
|
||||
for name, ps := range s.Properties {
|
||||
if _, ok := mv[name]; !ok {
|
||||
if ps.Generic.Default.Object != nil {
|
||||
mv[name] = clone(ps.Generic.Default.Object)
|
||||
}
|
||||
}
|
||||
if cur, ok := mv[name]; ok {
|
||||
cv, err := applyDefaults(cur, &ps, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mv[name] = cv
|
||||
}
|
||||
}
|
||||
|
||||
if s.AdditionalProperties != nil && s.AdditionalProperties.Structural != nil {
|
||||
ap := s.AdditionalProperties.Structural
|
||||
for k, cur := range mv {
|
||||
if _, isKnown := s.Properties[k]; isKnown {
|
||||
continue
|
||||
}
|
||||
cv, err := applyDefaults(cur, ap, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mv[k] = cv
|
||||
}
|
||||
}
|
||||
return mv, nil
|
||||
|
||||
case "array":
|
||||
sl, isSlice := v.([]any)
|
||||
if !isSlice || v == nil {
|
||||
if s.Generic.Default.Object != nil {
|
||||
if ds, ok := s.Generic.Default.Object.([]any); ok {
|
||||
sl = cloneSlice(ds)
|
||||
}
|
||||
}
|
||||
if sl == nil {
|
||||
sl = []any{}
|
||||
}
|
||||
}
|
||||
if s.Items != nil {
|
||||
for i := range sl {
|
||||
cv, err := applyDefaults(sl[i], s.Items, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sl[i] = cv
|
||||
}
|
||||
}
|
||||
return sl, nil
|
||||
|
||||
default:
|
||||
if v == nil && s.Generic.Default.Object != nil {
|
||||
return clone(s.Generic.Default.Object), nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func clone(x any) any {
|
||||
switch t := x.(type) {
|
||||
case map[string]any:
|
||||
return cloneMap(t)
|
||||
case []any:
|
||||
return cloneSlice(t)
|
||||
default:
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
func cloneMap(m map[string]any) map[string]any {
|
||||
out := make(map[string]any, len(m))
|
||||
for k, v := range m {
|
||||
out[k] = clone(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func cloneSlice(s []any) []any {
|
||||
out := make([]any, len(s))
|
||||
for i := range s {
|
||||
out[i] = clone(s[i])
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
apischema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
)
|
||||
|
||||
func TestApplication(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Application defaulting Suite")
|
||||
}
|
||||
|
||||
var _ = Describe("defaultLikeKubernetes", func() {
|
||||
var rootSchema *apischema.Structural
|
||||
|
||||
BeforeEach(func() {
|
||||
rootSchema = buildTestSchema()
|
||||
})
|
||||
|
||||
It("applies value-schema defaults to existing map key without merging parent object default", func() {
|
||||
spec := map[string]any{
|
||||
"nodeGroups": map[string]any{
|
||||
"md0": map[string]any{
|
||||
"minReplicas": 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := defaultLikeKubernetes(&spec, rootSchema)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
ng := spec["nodeGroups"].(map[string]any)["md0"].(map[string]any)
|
||||
|
||||
Expect(ng).To(HaveKeyWithValue("minReplicas", BeNumerically("==", 3)))
|
||||
Expect(ng).To(HaveKeyWithValue("instanceType", "u1.medium"))
|
||||
Expect(ng["roles"]).To(ConsistOf("ingress-nginx"))
|
||||
|
||||
Expect(ng).NotTo(HaveKey("ephemeralStorage"))
|
||||
Expect(ng).NotTo(HaveKey("maxReplicas"))
|
||||
Expect(ng).NotTo(HaveKey("resources"))
|
||||
})
|
||||
|
||||
It("does not create new map keys from parent object default", func() {
|
||||
spec := map[string]any{
|
||||
"nodeGroups": map[string]any{},
|
||||
}
|
||||
|
||||
err := defaultLikeKubernetes(&spec, rootSchema)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
ng := spec["nodeGroups"].(map[string]any)
|
||||
Expect(ng).NotTo(HaveKey("md0"))
|
||||
})
|
||||
})
|
||||
|
||||
func buildTestSchema() *apischema.Structural {
|
||||
instanceType := apischema.Structural{
|
||||
Generic: apischema.Generic{
|
||||
Type: "string",
|
||||
Default: apischema.JSON{Object: "u1.medium"},
|
||||
},
|
||||
}
|
||||
roles := apischema.Structural{
|
||||
Generic: apischema.Generic{
|
||||
Type: "array",
|
||||
Default: apischema.JSON{Object: []any{"ingress-nginx"}},
|
||||
},
|
||||
Items: &apischema.Structural{
|
||||
Generic: apischema.Generic{Type: "string"},
|
||||
},
|
||||
}
|
||||
minReplicas := apischema.Structural{
|
||||
Generic: apischema.Generic{Type: "integer"},
|
||||
}
|
||||
ephemeralStorage := apischema.Structural{
|
||||
Generic: apischema.Generic{Type: "string"},
|
||||
}
|
||||
maxReplicas := apischema.Structural{
|
||||
Generic: apischema.Generic{Type: "integer"},
|
||||
}
|
||||
resources := apischema.Structural{
|
||||
Generic: apischema.Generic{Type: "object"},
|
||||
Properties: map[string]apischema.Structural{},
|
||||
}
|
||||
|
||||
nodeGroupsValue := &apischema.Structural{
|
||||
Generic: apischema.Generic{Type: "object"},
|
||||
Properties: map[string]apischema.Structural{
|
||||
"instanceType": instanceType,
|
||||
"roles": roles,
|
||||
"minReplicas": minReplicas,
|
||||
"ephemeralStorage": ephemeralStorage,
|
||||
"maxReplicas": maxReplicas,
|
||||
"resources": resources,
|
||||
},
|
||||
}
|
||||
|
||||
nodeGroups := apischema.Structural{
|
||||
Generic: apischema.Generic{
|
||||
Type: "object",
|
||||
Default: apischema.JSON{Object: map[string]any{
|
||||
"md0": map[string]any{
|
||||
"ephemeralStorage": "20Gi",
|
||||
"maxReplicas": 10,
|
||||
"minReplicas": 0,
|
||||
"resources": map[string]any{},
|
||||
},
|
||||
}},
|
||||
},
|
||||
AdditionalProperties: &apischema.StructuralOrBool{
|
||||
Structural: nodeGroupsValue,
|
||||
},
|
||||
}
|
||||
|
||||
return &apischema.Structural{
|
||||
Generic: apischema.Generic{Type: "object"},
|
||||
Properties: map[string]apischema.Structural{
|
||||
"nodeGroups": nodeGroups,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -22,15 +22,9 @@ kubectl get helmreleases.helm.toolkit.fluxcd.io -A \
|
||||
|
||||
# JSON Patch
|
||||
| [
|
||||
(if $s.chart.spec.version? then
|
||||
{op:"replace", path:"/spec/chart/spec/version", value:"0.7.0"}
|
||||
else
|
||||
{op:"add", path:"/spec/chart/spec/version", value:"0.7.0"}
|
||||
end),
|
||||
{ op:"add", path:"/spec/chart/spec/version", value:"0.7.0" },
|
||||
|
||||
(if ($v|type) != "object" then {op:"add", path:"/spec/values", value:{}} else empty end),
|
||||
|
||||
(if ($v.volume?|type) != "object" then {op:"add", path:"/spec/values/volume", value:{}} else empty end),
|
||||
(if ($v.volume? | type) != "object" then {op:"add", path:"/spec/values/volume", value:{}} else empty end),
|
||||
|
||||
(if $v.size? then {op:"add", path:"/spec/values/volume/size", value:$v.size} else empty end),
|
||||
(if $v.storageClass? then {op:"add", path:"/spec/values/volume/storageClass", value:$v.storageClass} else empty end),
|
||||
|
||||
Reference in New Issue
Block a user