Compare commits

..

6 Commits

Author SHA1 Message Date
Andrei Kvapil
488b1bf27b build(cozyctl): add Makefile build and asset targets
Add cozyctl build target and cross-platform asset packaging targets
following the same pattern as cozypkg.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-02-23 17:47:45 +01:00
Andrei Kvapil
f4e9660b43 feat(cozyctl): add VM commands (console, vnc, migrate, port-forward)
VM interaction commands that resolve application type to VM name via
ApplicationDefinition discovery and exec virtctl for the actual work.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-02-23 17:47:45 +01:00
Andrei Kvapil
d9cfd5ac9e feat(cozyctl): add get command for applications and sub-resources
Dispatch logic for all resource types:
- Built-in: ns, modules, pvc
- Sub-resources: secrets, services, ingresses, workloads with -t flag
- Application types via dynamic ApplicationDefinition discovery

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-02-23 17:47:45 +01:00
Andrei Kvapil
5762ac4139 feat(cozyctl): add tabwriter-based resource printer
Output formatting for applications, namespaces, modules, PVCs,
secrets, services, ingresses, and workload monitors.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-02-23 17:47:45 +01:00
Andrei Kvapil
38f446c0d3 feat(cozyctl): add ApplicationDefinition discovery registry
Discover ApplicationDefinitions from the cluster and build a lookup
registry by plural/singular/kind names for dynamic resource resolution.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-02-23 17:47:45 +01:00
Andrei Kvapil
4de8e91864 feat(cozyctl): add CLI skeleton with root command and client factory
Introduce the cozyctl CLI tool for managing Cozystack applications
from the terminal. This initial commit includes:
- main.go entry point
- Cobra root command with --kubeconfig, --context, -n flags
- K8s client factory (typed + dynamic) with namespace resolution

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-02-23 17:47:44 +01:00
56 changed files with 1255 additions and 1573 deletions

View File

@@ -58,7 +58,10 @@ manifests:
cozypkg:
go build -ldflags "-X github.com/cozystack/cozystack/cmd/cozypkg/cmd.Version=v$(COZYSTACK_VERSION)" -o _out/bin/cozypkg ./cmd/cozypkg
assets: assets-talos assets-cozypkg
cozyctl:
go build -ldflags "-X github.com/cozystack/cozystack/cmd/cozyctl/cmd.Version=v$(COZYSTACK_VERSION)" -o _out/bin/cozyctl ./cmd/cozyctl
assets: assets-talos assets-cozypkg assets-cozyctl
assets-talos:
make -C packages/core/talos assets
@@ -73,6 +76,16 @@ assets-cozypkg-%:
cp LICENSE _out/bin/cozypkg-$*/LICENSE
tar -C _out/bin/cozypkg-$* -czf _out/assets/cozypkg-$*.tar.gz LICENSE cozypkg$(EXT)
assets-cozyctl: assets-cozyctl-linux-amd64 assets-cozyctl-linux-arm64 assets-cozyctl-darwin-amd64 assets-cozyctl-darwin-arm64 assets-cozyctl-windows-amd64 assets-cozyctl-windows-arm64
(cd _out/assets/ && sha256sum cozyctl-*.tar.gz) > _out/assets/cozyctl-checksums.txt
assets-cozyctl-%:
$(eval EXT := $(if $(filter windows,$(firstword $(subst -, ,$*))),.exe,))
mkdir -p _out/assets
GOOS=$(firstword $(subst -, ,$*)) GOARCH=$(lastword $(subst -, ,$*)) go build -ldflags "-X github.com/cozystack/cozystack/cmd/cozyctl/cmd.Version=v$(COZYSTACK_VERSION)" -o _out/bin/cozyctl-$*/cozyctl$(EXT) ./cmd/cozyctl
cp LICENSE _out/bin/cozyctl-$*/LICENSE
tar -C _out/bin/cozyctl-$* -czf _out/assets/cozyctl-$*.tar.gz LICENSE cozyctl$(EXT)
test:
make -C packages/core/testing apply
make -C packages/core/testing test

114
cmd/cozyctl/cmd/client.go Normal file
View File

@@ -0,0 +1,114 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"fmt"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/dynamic"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func buildRestConfig() (*rest.Config, error) {
rules := clientcmd.NewDefaultClientConfigLoadingRules()
if globalFlags.kubeconfig != "" {
rules.ExplicitPath = globalFlags.kubeconfig
}
overrides := &clientcmd.ConfigOverrides{}
if globalFlags.context != "" {
overrides.CurrentContext = globalFlags.context
}
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides).ClientConfig()
if err != nil {
return nil, fmt.Errorf("failed to load kubeconfig: %w", err)
}
return config, nil
}
func newScheme() *runtime.Scheme {
scheme := runtime.NewScheme()
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(cozyv1alpha1.AddToScheme(scheme))
return scheme
}
func newClients() (client.Client, dynamic.Interface, error) {
config, err := buildRestConfig()
if err != nil {
return nil, nil, err
}
scheme := newScheme()
typedClient, err := client.New(config, client.Options{Scheme: scheme})
if err != nil {
return nil, nil, fmt.Errorf("failed to create k8s client: %w", err)
}
dynClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, nil, fmt.Errorf("failed to create dynamic client: %w", err)
}
return typedClient, dynClient, nil
}
func getNamespace() (string, error) {
if globalFlags.namespace != "" {
return globalFlags.namespace, nil
}
rules := clientcmd.NewDefaultClientConfigLoadingRules()
if globalFlags.kubeconfig != "" {
rules.ExplicitPath = globalFlags.kubeconfig
}
overrides := &clientcmd.ConfigOverrides{}
if globalFlags.context != "" {
overrides.CurrentContext = globalFlags.context
}
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides)
ns, _, err := clientConfig.Namespace()
if err != nil {
return "", fmt.Errorf("failed to determine namespace: %w", err)
}
if ns == "" {
ns = "default"
}
return ns, nil
}
// getRestConfig is a convenience function when only the rest.Config is needed
// (used by buildRestConfig but also available for other callers).
func getRestConfig() (*rest.Config, error) {
if globalFlags.kubeconfig != "" || globalFlags.context != "" {
return buildRestConfig()
}
config, err := ctrl.GetConfig()
if err != nil {
return nil, fmt.Errorf("failed to get kubeconfig: %w", err)
}
return config, nil
}

View File

@@ -0,0 +1,43 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"github.com/spf13/cobra"
)
var consoleCmd = &cobra.Command{
Use: "console <type> <name>",
Short: "Open a serial console to a VirtualMachine",
Long: `Open a serial console to a VirtualMachine using virtctl. Only valid for VirtualMachine or VMInstance kinds.`,
Args: cobra.ExactArgs(2),
RunE: runConsole,
}
func init() {
rootCmd.AddCommand(consoleCmd)
}
func runConsole(cmd *cobra.Command, args []string) error {
vmName, ns, err := resolveVMArgs(args)
if err != nil {
return err
}
virtctlArgs := []string{"virtctl", "console", vmName, "-n", ns}
return execVirtctl(virtctlArgs)
}

View File

@@ -0,0 +1,112 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"context"
"fmt"
"strings"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// AppDefInfo holds resolved information about an ApplicationDefinition.
type AppDefInfo struct {
Name string // e.g. "postgres"
Kind string // e.g. "Postgres"
Plural string // e.g. "postgreses"
Singular string // e.g. "postgres"
Prefix string // e.g. "postgres-"
IsModule bool
}
// AppDefRegistry provides fast lookup of ApplicationDefinitions by plural, singular, or kind.
type AppDefRegistry struct {
byPlural map[string]*AppDefInfo
bySingular map[string]*AppDefInfo
byKind map[string]*AppDefInfo
all []*AppDefInfo
}
// discoverAppDefs lists all ApplicationDefinitions from the cluster and builds a registry.
func discoverAppDefs(ctx context.Context, typedClient client.Client) (*AppDefRegistry, error) {
var list cozyv1alpha1.ApplicationDefinitionList
if err := typedClient.List(ctx, &list); err != nil {
return nil, fmt.Errorf("failed to list ApplicationDefinitions: %w", err)
}
reg := &AppDefRegistry{
byPlural: make(map[string]*AppDefInfo),
bySingular: make(map[string]*AppDefInfo),
byKind: make(map[string]*AppDefInfo),
}
for i := range list.Items {
ad := &list.Items[i]
info := &AppDefInfo{
Name: ad.Name,
Kind: ad.Spec.Application.Kind,
Plural: ad.Spec.Application.Plural,
Singular: ad.Spec.Application.Singular,
Prefix: ad.Spec.Release.Prefix,
IsModule: ad.Spec.Dashboard != nil && ad.Spec.Dashboard.Module,
}
reg.all = append(reg.all, info)
reg.byPlural[strings.ToLower(info.Plural)] = info
reg.bySingular[strings.ToLower(info.Singular)] = info
reg.byKind[strings.ToLower(info.Kind)] = info
}
return reg, nil
}
// Resolve looks up an AppDefInfo by name (case-insensitive), checking plural, singular, then kind.
func (r *AppDefRegistry) Resolve(name string) *AppDefInfo {
lower := strings.ToLower(name)
if info, ok := r.byPlural[lower]; ok {
return info
}
if info, ok := r.bySingular[lower]; ok {
return info
}
if info, ok := r.byKind[lower]; ok {
return info
}
return nil
}
// ResolveModule looks up an AppDefInfo among modules only.
func (r *AppDefRegistry) ResolveModule(name string) *AppDefInfo {
lower := strings.ToLower(name)
for _, info := range r.all {
if !info.IsModule {
continue
}
if strings.ToLower(info.Plural) == lower ||
strings.ToLower(info.Singular) == lower ||
strings.ToLower(info.Kind) == lower {
return info
}
}
return nil
}
// All returns all discovered AppDefInfo entries.
func (r *AppDefRegistry) All() []*AppDefInfo {
return r.all
}

361
cmd/cozyctl/cmd/get.go Normal file
View File

@@ -0,0 +1,361 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"context"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1"
corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var getCmdFlags struct {
target string
}
var getCmd = &cobra.Command{
Use: "get <type> [name]",
Short: "Display one or many resources",
Long: `Display one or many resources.
Built-in types:
ns, namespaces Tenant namespaces (cluster-scoped)
modules Tenant modules
pvc, pvcs PersistentVolumeClaims
Sub-resource types (use -t to filter by parent application):
secrets Secrets
services, svc Services
ingresses, ing Ingresses
workloads WorkloadMonitors
Application types are discovered dynamically from ApplicationDefinitions.
Use -t type/name to filter sub-resources by a specific application.`,
Args: cobra.RangeArgs(1, 2),
RunE: runGet,
}
func init() {
rootCmd.AddCommand(getCmd)
getCmd.Flags().StringVarP(&getCmdFlags.target, "target", "t", "", "Filter sub-resources by application type/name")
}
func runGet(cmd *cobra.Command, args []string) error {
ctx := context.Background()
resourceType := args[0]
var resourceName string
if len(args) > 1 {
resourceName = args[1]
}
switch strings.ToLower(resourceType) {
case "ns", "namespace", "namespaces":
return getNamespaces(ctx, resourceName)
case "module", "modules":
return getModules(ctx, resourceName)
case "pvc", "pvcs", "persistentvolumeclaim", "persistentvolumeclaims":
return getPVCs(ctx, resourceName)
case "secret", "secrets":
return getSubResources(ctx, "secrets", resourceName)
case "service", "services", "svc":
return getSubResources(ctx, "services", resourceName)
case "ingress", "ingresses", "ing":
return getSubResources(ctx, "ingresses", resourceName)
case "workload", "workloads":
return getSubResources(ctx, "workloads", resourceName)
default:
return getApplications(ctx, resourceType, resourceName)
}
}
func getNamespaces(ctx context.Context, name string) error {
_, dynClient, err := newClients()
if err != nil {
return err
}
gvr := schema.GroupVersionResource{Group: "core.cozystack.io", Version: "v1alpha1", Resource: "tenantnamespaces"}
if name != "" {
item, err := dynClient.Resource(gvr).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get namespace %q: %w", name, err)
}
printNamespaces([]unstructured.Unstructured{*item})
return nil
}
list, err := dynClient.Resource(gvr).List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list namespaces: %w", err)
}
if len(list.Items) == 0 {
printNoResources(os.Stderr, "namespaces")
return nil
}
printNamespaces(list.Items)
return nil
}
func getModules(ctx context.Context, name string) error {
_, dynClient, err := newClients()
if err != nil {
return err
}
ns, err := getNamespace()
if err != nil {
return err
}
gvr := schema.GroupVersionResource{Group: "core.cozystack.io", Version: "v1alpha1", Resource: "tenantmodules"}
if name != "" {
item, err := dynClient.Resource(gvr).Namespace(ns).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get module %q: %w", name, err)
}
printModules([]unstructured.Unstructured{*item})
return nil
}
list, err := dynClient.Resource(gvr).Namespace(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list modules: %w", err)
}
if len(list.Items) == 0 {
printNoResources(os.Stderr, "modules")
return nil
}
printModules(list.Items)
return nil
}
func getPVCs(ctx context.Context, name string) error {
_, dynClient, err := newClients()
if err != nil {
return err
}
ns, err := getNamespace()
if err != nil {
return err
}
gvr := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolumeclaims"}
if name != "" {
item, err := dynClient.Resource(gvr).Namespace(ns).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get PVC %q: %w", name, err)
}
printPVCs([]unstructured.Unstructured{*item})
return nil
}
list, err := dynClient.Resource(gvr).Namespace(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list PVCs: %w", err)
}
if len(list.Items) == 0 {
printNoResources(os.Stderr, "PVCs")
return nil
}
printPVCs(list.Items)
return nil
}
func getSubResources(ctx context.Context, subType string, name string) error {
typedClient, dynClient, err := newClients()
if err != nil {
return err
}
ns, err := getNamespace()
if err != nil {
return err
}
labelSelector, err := buildSubResourceSelector(ctx, typedClient, getCmdFlags.target)
if err != nil {
return err
}
switch subType {
case "secrets":
return getFilteredSecrets(ctx, dynClient, ns, name, labelSelector)
case "services":
return getFilteredServices(ctx, dynClient, ns, name, labelSelector)
case "ingresses":
return getFilteredIngresses(ctx, dynClient, ns, name, labelSelector)
case "workloads":
return getFilteredWorkloads(ctx, dynClient, ns, name, labelSelector)
default:
return fmt.Errorf("unknown sub-resource type: %s", subType)
}
}
func buildSubResourceSelector(ctx context.Context, typedClient client.Client, target string) (string, error) {
var selectors []string
if target == "" {
selectors = append(selectors, corev1alpha1.TenantResourceLabelKey+"="+corev1alpha1.TenantResourceLabelValue)
return strings.Join(selectors, ","), nil
}
parts := strings.SplitN(target, "/", 2)
if len(parts) != 2 {
return "", fmt.Errorf("invalid target format %q, expected type/name", target)
}
targetType, targetName := parts[0], parts[1]
// Discover ApplicationDefinitions to resolve the target type
registry, err := discoverAppDefs(ctx, typedClient)
if err != nil {
return "", err
}
// Check if this is a module reference
if strings.ToLower(targetType) == "module" {
info := registry.ResolveModule(targetName)
if info == nil {
return "", fmt.Errorf("unknown module %q", targetName)
}
selectors = append(selectors,
appsv1alpha1.ApplicationKindLabel+"="+info.Kind,
appsv1alpha1.ApplicationNameLabel+"="+targetName,
corev1alpha1.TenantResourceLabelKey+"="+corev1alpha1.TenantResourceLabelValue,
)
return strings.Join(selectors, ","), nil
}
info := registry.Resolve(targetType)
if info == nil {
return "", fmt.Errorf("unknown application type %q", targetType)
}
selectors = append(selectors,
appsv1alpha1.ApplicationKindLabel+"="+info.Kind,
appsv1alpha1.ApplicationNameLabel+"="+targetName,
corev1alpha1.TenantResourceLabelKey+"="+corev1alpha1.TenantResourceLabelValue,
)
return strings.Join(selectors, ","), nil
}
func getFilteredSecrets(ctx context.Context, dynClient dynamic.Interface, ns, name, labelSelector string) error {
gvr := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
return getFilteredResources(ctx, dynClient, gvr, ns, name, labelSelector, "secrets", printSecrets)
}
func getFilteredServices(ctx context.Context, dynClient dynamic.Interface, ns, name, labelSelector string) error {
gvr := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"}
return getFilteredResources(ctx, dynClient, gvr, ns, name, labelSelector, "services", printServices)
}
func getFilteredIngresses(ctx context.Context, dynClient dynamic.Interface, ns, name, labelSelector string) error {
gvr := schema.GroupVersionResource{Group: "networking.k8s.io", Version: "v1", Resource: "ingresses"}
return getFilteredResources(ctx, dynClient, gvr, ns, name, labelSelector, "ingresses", printIngresses)
}
func getFilteredWorkloads(ctx context.Context, dynClient dynamic.Interface, ns, name, labelSelector string) error {
gvr := schema.GroupVersionResource{Group: "cozystack.io", Version: "v1alpha1", Resource: "workloadmonitors"}
return getFilteredResources(ctx, dynClient, gvr, ns, name, labelSelector, "workloads", printWorkloads)
}
func getFilteredResources(
ctx context.Context,
dynClient dynamic.Interface,
gvr schema.GroupVersionResource,
ns, name, labelSelector string,
typeName string,
printer func([]unstructured.Unstructured),
) error {
if name != "" {
item, err := dynClient.Resource(gvr).Namespace(ns).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get %s %q: %w", typeName, name, err)
}
printer([]unstructured.Unstructured{*item})
return nil
}
list, err := dynClient.Resource(gvr).Namespace(ns).List(ctx, metav1.ListOptions{
LabelSelector: labelSelector,
})
if err != nil {
return fmt.Errorf("failed to list %s: %w", typeName, err)
}
if len(list.Items) == 0 {
printNoResources(os.Stderr, typeName)
return nil
}
printer(list.Items)
return nil
}
func getApplications(ctx context.Context, resourceType, name string) error {
typedClient, dynClient, err := newClients()
if err != nil {
return err
}
ns, err := getNamespace()
if err != nil {
return err
}
registry, err := discoverAppDefs(ctx, typedClient)
if err != nil {
return err
}
info := registry.Resolve(resourceType)
if info == nil {
return fmt.Errorf("unknown resource type %q\nUse 'cozyctl get --help' for available types", resourceType)
}
gvr := schema.GroupVersionResource{Group: "apps.cozystack.io", Version: "v1alpha1", Resource: info.Plural}
if name != "" {
item, err := dynClient.Resource(gvr).Namespace(ns).Get(ctx, name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get %s %q: %w", info.Singular, name, err)
}
printApplications([]unstructured.Unstructured{*item})
return nil
}
list, err := dynClient.Resource(gvr).Namespace(ns).List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list %s: %w", info.Plural, err)
}
if len(list.Items) == 0 {
printNoResources(os.Stderr, info.Plural)
return nil
}
printApplications(list.Items)
return nil
}

View File

@@ -0,0 +1,43 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"github.com/spf13/cobra"
)
var migrateCmd = &cobra.Command{
Use: "migrate <type> <name>",
Short: "Live-migrate a VirtualMachine to another node",
Long: `Live-migrate a VirtualMachine to another node using virtctl. Only valid for VirtualMachine or VMInstance kinds.`,
Args: cobra.ExactArgs(2),
RunE: runMigrate,
}
func init() {
rootCmd.AddCommand(migrateCmd)
}
func runMigrate(cmd *cobra.Command, args []string) error {
vmName, ns, err := resolveVMArgs(args)
if err != nil {
return err
}
virtctlArgs := []string{"virtctl", "migrate", vmName, "-n", ns}
return execVirtctl(virtctlArgs)
}

View File

@@ -0,0 +1,51 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var portForwardCmd = &cobra.Command{
Use: "port-forward <type/name> [ports...]",
Short: "Forward ports to a VirtualMachineInstance",
Long: `Forward ports to a VirtualMachineInstance using virtctl. Only valid for VirtualMachine or VMInstance kinds.`,
Args: cobra.MinimumNArgs(2),
RunE: runPortForward,
}
func init() {
rootCmd.AddCommand(portForwardCmd)
}
func runPortForward(cmd *cobra.Command, args []string) error {
vmName, ns, err := resolveVMArgs(args[:1])
if err != nil {
return err
}
ports := args[1:]
if len(ports) == 0 {
return fmt.Errorf("at least one port is required")
}
virtctlArgs := []string{"virtctl", "port-forward", "vmi/" + vmName, "-n", ns}
virtctlArgs = append(virtctlArgs, ports...)
return execVirtctl(virtctlArgs)
}

250
cmd/cozyctl/cmd/printer.go Normal file
View File

@@ -0,0 +1,250 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func newTabWriter() *tabwriter.Writer {
return tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
}
func printApplications(items []unstructured.Unstructured) {
w := newTabWriter()
defer w.Flush()
fmt.Fprintln(w, "NAME\tVERSION\tREADY\tSTATUS")
for _, item := range items {
name := item.GetName()
version, _, _ := unstructured.NestedString(item.Object, "appVersion")
ready, status := extractCondition(item)
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, version, ready, truncate(status, 48))
}
}
func printNamespaces(items []unstructured.Unstructured) {
w := newTabWriter()
defer w.Flush()
fmt.Fprintln(w, "NAME")
for _, item := range items {
fmt.Fprintln(w, item.GetName())
}
}
func printModules(items []unstructured.Unstructured) {
w := newTabWriter()
defer w.Flush()
fmt.Fprintln(w, "NAME\tVERSION\tREADY\tSTATUS")
for _, item := range items {
name := item.GetName()
version, _, _ := unstructured.NestedString(item.Object, "appVersion")
ready, status := extractCondition(item)
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, version, ready, truncate(status, 48))
}
}
func printPVCs(items []unstructured.Unstructured) {
w := newTabWriter()
defer w.Flush()
fmt.Fprintln(w, "NAME\tSTATUS\tVOLUME\tCAPACITY\tSTORAGECLASS")
for _, item := range items {
name := item.GetName()
phase, _, _ := unstructured.NestedString(item.Object, "status", "phase")
volume, _, _ := unstructured.NestedString(item.Object, "spec", "volumeName")
capacity := ""
if cap, ok, _ := unstructured.NestedStringMap(item.Object, "status", "capacity"); ok {
capacity = cap["storage"]
}
sc, _, _ := unstructured.NestedString(item.Object, "spec", "storageClassName")
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", name, phase, volume, capacity, sc)
}
}
func printSecrets(items []unstructured.Unstructured) {
w := newTabWriter()
defer w.Flush()
fmt.Fprintln(w, "NAME\tTYPE\tDATA")
for _, item := range items {
name := item.GetName()
secretType, _, _ := unstructured.NestedString(item.Object, "type")
data, _, _ := unstructured.NestedMap(item.Object, "data")
fmt.Fprintf(w, "%s\t%s\t%d\n", name, secretType, len(data))
}
}
func printServices(items []unstructured.Unstructured) {
w := newTabWriter()
defer w.Flush()
fmt.Fprintln(w, "NAME\tTYPE\tCLUSTER-IP\tEXTERNAL-IP\tPORTS")
for _, item := range items {
name := item.GetName()
svcType, _, _ := unstructured.NestedString(item.Object, "spec", "type")
clusterIP, _, _ := unstructured.NestedString(item.Object, "spec", "clusterIP")
externalIP := "<none>"
if lbIngress, ok, _ := unstructured.NestedSlice(item.Object, "status", "loadBalancer", "ingress"); ok && len(lbIngress) > 0 {
var ips []string
for _, ingress := range lbIngress {
if m, ok := ingress.(map[string]interface{}); ok {
if ip, ok := m["ip"].(string); ok {
ips = append(ips, ip)
} else if hostname, ok := m["hostname"].(string); ok {
ips = append(ips, hostname)
}
}
}
if len(ips) > 0 {
externalIP = strings.Join(ips, ",")
}
}
ports := formatPorts(item)
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", name, svcType, clusterIP, externalIP, ports)
}
}
func printIngresses(items []unstructured.Unstructured) {
w := newTabWriter()
defer w.Flush()
fmt.Fprintln(w, "NAME\tCLASS\tHOSTS\tADDRESS")
for _, item := range items {
name := item.GetName()
class, _, _ := unstructured.NestedString(item.Object, "spec", "ingressClassName")
var hosts []string
if rules, ok, _ := unstructured.NestedSlice(item.Object, "spec", "rules"); ok {
for _, rule := range rules {
if m, ok := rule.(map[string]interface{}); ok {
if host, ok := m["host"].(string); ok {
hosts = append(hosts, host)
}
}
}
}
hostsStr := "<none>"
if len(hosts) > 0 {
hostsStr = strings.Join(hosts, ",")
}
address := ""
if lbIngress, ok, _ := unstructured.NestedSlice(item.Object, "status", "loadBalancer", "ingress"); ok && len(lbIngress) > 0 {
var addrs []string
for _, ingress := range lbIngress {
if m, ok := ingress.(map[string]interface{}); ok {
if ip, ok := m["ip"].(string); ok {
addrs = append(addrs, ip)
} else if hostname, ok := m["hostname"].(string); ok {
addrs = append(addrs, hostname)
}
}
}
address = strings.Join(addrs, ",")
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, class, hostsStr, address)
}
}
func printWorkloads(items []unstructured.Unstructured) {
w := newTabWriter()
defer w.Flush()
fmt.Fprintln(w, "NAME\tKIND\tTYPE\tVERSION\tAVAILABLE\tOBSERVED\tOPERATIONAL")
for _, item := range items {
name := item.GetName()
kind, _, _ := unstructured.NestedString(item.Object, "spec", "kind")
wType, _, _ := unstructured.NestedString(item.Object, "spec", "type")
version, _, _ := unstructured.NestedString(item.Object, "spec", "version")
available, _, _ := unstructured.NestedInt64(item.Object, "status", "availableReplicas")
observed, _, _ := unstructured.NestedInt64(item.Object, "status", "observedReplicas")
operational, ok, _ := unstructured.NestedBool(item.Object, "status", "operational")
opStr := ""
if ok {
opStr = fmt.Sprintf("%t", operational)
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%d\t%s\n", name, kind, wType, version, available, observed, opStr)
}
}
func printNoResources(w io.Writer, resourceType string) {
fmt.Fprintf(w, "No %s found\n", resourceType)
}
func extractCondition(item unstructured.Unstructured) (string, string) {
conditions, ok, _ := unstructured.NestedSlice(item.Object, "status", "conditions")
if !ok {
return "Unknown", ""
}
for _, c := range conditions {
cond, ok := c.(map[string]interface{})
if !ok {
continue
}
if cond["type"] == "Ready" {
ready, _ := cond["status"].(string)
message, _ := cond["message"].(string)
return ready, message
}
}
return "Unknown", ""
}
func truncate(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen-3] + "..."
}
func formatPorts(item unstructured.Unstructured) string {
ports, ok, _ := unstructured.NestedSlice(item.Object, "spec", "ports")
if !ok || len(ports) == 0 {
return "<none>"
}
var parts []string
for _, p := range ports {
port, ok := p.(map[string]interface{})
if !ok {
continue
}
portNum, _, _ := unstructured.NestedInt64(port, "port")
protocol, _, _ := unstructured.NestedString(port, "protocol")
if protocol == "" {
protocol = "TCP"
}
nodePort, _, _ := unstructured.NestedInt64(port, "nodePort")
if nodePort > 0 {
parts = append(parts, fmt.Sprintf("%d:%d/%s", portNum, nodePort, protocol))
} else {
parts = append(parts, fmt.Sprintf("%d/%s", portNum, protocol))
}
}
return strings.Join(parts, ",")
}

57
cmd/cozyctl/cmd/root.go Normal file
View File

@@ -0,0 +1,57 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
// Version is set at build time via -ldflags.
var Version = "dev"
var globalFlags struct {
kubeconfig string
context string
namespace string
}
var rootCmd = &cobra.Command{
Use: "cozyctl",
Short: "A CLI for managing Cozystack applications",
SilenceErrors: true,
SilenceUsage: true,
DisableAutoGenTag: true,
}
// Execute adds all child commands to the root command and sets flags appropriately.
func Execute() error {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
return err
}
return nil
}
func init() {
rootCmd.Version = Version
rootCmd.PersistentFlags().StringVar(&globalFlags.kubeconfig, "kubeconfig", "", "Path to kubeconfig file")
rootCmd.PersistentFlags().StringVar(&globalFlags.context, "context", "", "Kubernetes context to use")
rootCmd.PersistentFlags().StringVarP(&globalFlags.namespace, "namespace", "n", "", "Kubernetes namespace")
}

106
cmd/cozyctl/cmd/vm.go Normal file
View File

@@ -0,0 +1,106 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"syscall"
)
// vmKindPrefix maps application Kind to the release prefix used by KubeVirt VMs.
func vmKindPrefix(kind string) (string, bool) {
switch kind {
case "VirtualMachine":
return "virtual-machine", true
case "VMInstance":
return "vm-instance", true
default:
return "", false
}
}
// resolveVMArgs takes CLI args (type, name or type/name), resolves the application type
// via discovery, validates it's a VM kind, and returns the full VM name and namespace.
func resolveVMArgs(args []string) (string, string, error) {
var resourceType, resourceName string
if len(args) == 1 {
// type/name format
parts := strings.SplitN(args[0], "/", 2)
if len(parts) != 2 {
return "", "", fmt.Errorf("expected type/name format, got %q", args[0])
}
resourceType, resourceName = parts[0], parts[1]
} else {
resourceType = args[0]
resourceName = args[1]
}
ctx := context.Background()
typedClient, _, err := newClients()
if err != nil {
return "", "", err
}
registry, err := discoverAppDefs(ctx, typedClient)
if err != nil {
return "", "", err
}
info := registry.Resolve(resourceType)
if info == nil {
return "", "", fmt.Errorf("unknown application type %q", resourceType)
}
prefix, ok := vmKindPrefix(info.Kind)
if !ok {
return "", "", fmt.Errorf("resource type %q (Kind=%s) is not a VirtualMachine or VMInstance", resourceType, info.Kind)
}
ns, err := getNamespace()
if err != nil {
return "", "", err
}
vmName := prefix + "-" + resourceName
return vmName, ns, nil
}
// execVirtctl replaces the current process with virtctl.
func execVirtctl(args []string) error {
virtctlPath, err := exec.LookPath("virtctl")
if err != nil {
return fmt.Errorf("virtctl not found in PATH: %w", err)
}
// Append kubeconfig/context flags if set
if globalFlags.kubeconfig != "" {
args = append(args, "--kubeconfig", globalFlags.kubeconfig)
}
if globalFlags.context != "" {
args = append(args, "--context", globalFlags.context)
}
if err := syscall.Exec(virtctlPath, args, os.Environ()); err != nil {
return fmt.Errorf("failed to exec virtctl: %w", err)
}
return nil
}

43
cmd/cozyctl/cmd/vnc.go Normal file
View File

@@ -0,0 +1,43 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"github.com/spf13/cobra"
)
var vncCmd = &cobra.Command{
Use: "vnc <type> <name>",
Short: "Open a VNC connection to a VirtualMachine",
Long: `Open a VNC connection to a VirtualMachine using virtctl. Only valid for VirtualMachine or VMInstance kinds.`,
Args: cobra.ExactArgs(2),
RunE: runVNC,
}
func init() {
rootCmd.AddCommand(vncCmd)
}
func runVNC(cmd *cobra.Command, args []string) error {
vmName, ns, err := resolveVMArgs(args)
if err != nil {
return err
}
virtctlArgs := []string{"virtctl", "vnc", vmName, "-n", ns}
return execVirtctl(virtctlArgs)
}

29
cmd/cozyctl/main.go Normal file
View File

@@ -0,0 +1,29 @@
/*
Copyright 2025 The Cozystack Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"os"
"github.com/cozystack/cozystack/cmd/cozyctl/cmd"
)
func main() {
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}

View File

@@ -1,15 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.40.5
-->
## Improvements
* **[dashboard] Improve dashboard session params**: Improved session parameter handling in the dashboard for better user experience and more reliable session management ([**@lllamnyp**](https://github.com/lllamnyp) in #1913, #1919).
## Dependencies
* **Update cozyhr to v1.6.1**: Updated cozyhr to v1.6.1, which fixes a critical bug causing helm-controller v0.37.0+ to unexpectedly uninstall HelmReleases after cozyhr apply by correcting history snapshot fields for helm-controller compatibility ([**@kvaps**](https://github.com/kvaps) in cozystack/cozyhr#10).
---
**Full Changelog**: [v0.40.4...v0.40.5](https://github.com/cozystack/cozystack/compare/v0.40.4...v0.40.5)

View File

@@ -1,11 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.40.6
-->
## Fixes
* **[kubernetes] Fix manifests for kubernetes deployment**: Fixed incorrect manifests that prevented proper Kubernetes deployment, restoring correct application behavior ([**@IvanHunters**](https://github.com/IvanHunters) in #1943, #1944).
---
**Full Changelog**: [v0.40.5...v0.40.6](https://github.com/cozystack/cozystack/compare/v0.40.5...v0.40.6)

View File

@@ -1,11 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.40.7
-->
## Security
* **[dashboard] Verify JWT token**: Added JWT token verification to the dashboard, ensuring that authentication tokens are properly validated before granting access. This prevents unauthorized access through forged or expired tokens ([**@lllamnyp**](https://github.com/lllamnyp) in #1980, #1984).
---
**Full Changelog**: [v0.40.6...v0.40.7](https://github.com/cozystack/cozystack/compare/v0.40.6...v0.40.7)

View File

@@ -1,11 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.41.4
-->
## Dependencies
* **Update cozyhr to v1.6.1**: Updated cozyhr to v1.6.1, which fixes a critical bug causing helm-controller v0.37.0+ to unexpectedly uninstall HelmReleases after cozyhr apply by correcting history snapshot fields for helm-controller compatibility ([**@kvaps**](https://github.com/kvaps) in cozystack/cozyhr#10).
---
**Full Changelog**: [v0.41.3...v0.41.4](https://github.com/cozystack/cozystack/compare/v0.41.3...v0.41.4)

View File

@@ -1,21 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.41.5
-->
## Features and Improvements
* **[dashboard] Add "Edit" button to all resources**: Added an "Edit" button across all resource views in the dashboard, allowing users to modify resource configurations directly from the UI ([**@sircthulhu**](https://github.com/sircthulhu) in #1928, #1931).
* **[dashboard] Add resource quota usage to tenant details page**: Added resource quota usage display to the tenant details page, giving administrators visibility into how much of allocated resources each tenant is consuming ([**@sircthulhu**](https://github.com/sircthulhu) in #1929, #1932).
* **[branding] Separate values for keycloak**: Separated Keycloak branding values into dedicated configuration, allowing more granular customization of Keycloak appearance without affecting other branding settings ([**@nbykov0**](https://github.com/nbykov0) in #1946).
* **Add instance profile label to workload monitor**: Added instance profile metadata labels to the workload monitor, enabling better resource tracking and monitoring by instance profile type ([**@matthieu-robin**](https://github.com/matthieu-robin) in #1954, #1957).
## Fixes
* **[kubernetes] Fix manifests for kubernetes deployment**: Fixed incorrect manifests that prevented proper Kubernetes deployment, restoring correct application behavior ([**@IvanHunters**](https://github.com/IvanHunters) in #1943, #1945).
---
**Full Changelog**: [v0.41.4...v0.41.5](https://github.com/cozystack/cozystack/compare/v0.41.4...v0.41.5)

View File

@@ -1,17 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.41.6
-->
## Improvements
* **[vm] Allow changing field external after creation**: Users can now modify the external network field on virtual machines after initial creation, providing more flexibility in VM networking configuration without requiring recreation ([**@sircthulhu**](https://github.com/sircthulhu) in #1956, #1962).
* **[branding] Separate values for keycloak**: Separated Keycloak branding values into dedicated configuration for more granular customization of Keycloak appearance ([**@nbykov0**](https://github.com/nbykov0) in #1947, #1963).
## Fixes
* **[kubernetes] Fix coredns serviceaccount to match kubernetes bootstrap RBAC**: Configured the CoreDNS chart to create a `kube-dns` ServiceAccount matching the Kubernetes bootstrap ClusterRoleBinding, fixing RBAC errors (`Failed to watch`) when CoreDNS pods restart ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #1958, #1978).
---
**Full Changelog**: [v0.41.5...v0.41.6](https://github.com/cozystack/cozystack/compare/v0.41.5...v0.41.6)

View File

@@ -1,15 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.41.7
-->
## Security
* **[dashboard] Verify JWT token**: Added JWT token verification to the dashboard, ensuring that authentication tokens are properly validated before granting access. This prevents unauthorized access through forged or expired tokens ([**@lllamnyp**](https://github.com/lllamnyp) in #1980, #1983).
## Fixes
* **[postgres-operator] Correct PromQL syntax in CNPGClusterOffline alert**: Fixed incorrect PromQL syntax in the `CNPGClusterOffline` alert rule for CloudNativePG, ensuring the alert fires correctly when all instances of a PostgreSQL cluster are offline ([**@mattia-eleuteri**](https://github.com/mattia-eleuteri) in #1981, #1989).
---
**Full Changelog**: [v0.41.6...v0.41.7](https://github.com/cozystack/cozystack/compare/v0.41.6...v0.41.7)

View File

@@ -1,17 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.41.8
-->
## Features and Improvements
* **[kubernetes] Auto-enable Gateway API support in cert-manager**: cert-manager now automatically enables `enableGatewayAPI` when the Gateway API addon is active in the Kubernetes application. Users no longer need to manually configure this setting, and the option can still be overridden via `valuesOverride` ([**@kvaps**](https://github.com/kvaps) in #1997, #2012).
* **[vm] Allow switching between instancetype and custom resources**: Users can now switch virtual machines between instancetype-based and custom resource configurations after creation. The upgrade hook atomically patches VM resources, providing more flexibility in adjusting VM sizing without recreation ([**@sircthulhu**](https://github.com/sircthulhu) in #2008, #2013).
## Fixes
* **[dashboard] Add startupProbe to prevent container restarts on slow hardware**: Added `startupProbe` to both `bff` and `web` containers in the dashboard deployment. On slow hardware, kubelet was killing containers because the `livenessProbe` only allowed ~33 seconds for startup. The `startupProbe` gives containers up to 60 seconds to start before `livenessProbe` kicks in ([**@kvaps**](https://github.com/kvaps) in #1996, #2014).
---
**Full Changelog**: [v0.41.7...v0.41.8](https://github.com/cozystack/cozystack/compare/v0.41.7...v0.41.8)

View File

@@ -1,15 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.41.9
-->
## Fixes
* **[cozystack-basics] Deny resourcequotas deletion for tenant admin**: Prevented tenant administrators from deleting resource quotas, ensuring that resource limits set by platform administrators cannot be bypassed by tenant-level users ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2076).
## Dependencies
* **Update Kube-OVN to v1.15.3**: Updated Kube-OVN CNI to v1.15.3 with latest bug fixes and improvements ([**@kvaps**](https://github.com/kvaps)).
---
**Full Changelog**: [v0.41.8...v0.41.9](https://github.com/cozystack/cozystack/compare/v0.41.8...v0.41.9)

View File

@@ -1,65 +0,0 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v1.0.0-rc.1
-->
> **⚠️ Release Candidate Warning**: This is a release candidate intended for final validation before the stable v1.0.0 release. Breaking changes are not expected at this stage, but please test thoroughly before deploying to production.
## Features and Improvements
* **[harbor] Add managed Harbor container registry**: Added Harbor v2.14.2 as a managed tenant-level container registry service. The application uses CloudNativePG for PostgreSQL, the Redis operator for caching, and S3 via COSI BucketClaim (from SeaweedFS) for registry image storage. Auto-generated admin credentials are persisted across upgrades, TLS is handled by cert-manager, and Trivy vulnerability scanner is included. Users can now deploy a fully managed, production-ready OCI container registry within their tenant ([**@lexfrei**](https://github.com/lexfrei) in #2058).
* **[kubernetes] Update supported Kubernetes versions to v1.30v1.35**: Updated the tenant Kubernetes version matrix to v1.30, v1.31, v1.32, v1.33, v1.34, and v1.35 (now the default). EOL versions v1.28 and v1.29 are removed. Kamaji is updated to edge-26.2.4 with full Kubernetes 1.35 support, and the CAPI Kamaji provider is updated to v0.16.0. A compatibility patch ensures kubelets older than v1.35 are not broken by Kamaji injecting 1.35-specific kubelet fields ([**@lexfrei**](https://github.com/lexfrei) in #2073).
* **[platform] Make cluster issuer name and ACME solver configurable**: Added `publishing.certificates.solver` (`http01` or `dns01`) and `publishing.certificates.issuerName` (default: `letsencrypt-prod`) parameters to the platform chart. This allows operators to point all ingress TLS annotations at any ClusterIssuer — custom ACME, self-signed, or internal CA — without modifying individual package templates. See the Breaking Changes section for the rename from the previous `issuerType` field ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2077).
* **[dashboard] VMInstance dropdowns for disks and instanceType**: The VM instance creation form now renders API-backed dropdowns for the `instanceType` field (populated from `VirtualMachineClusterInstancetype` cluster resources) and for disk `name` fields (populated from `VMDisk` resources in the same namespace). Default values are read from the ApplicationDefinition's OpenAPI schema. This eliminates manual lookups and reduces misconfiguration when attaching disks or selecting VM instance types ([**@sircthulhu**](https://github.com/sircthulhu) in #2071).
* **[installer] Remove CRDs from Helm chart, delegate lifecycle to operator**: The `cozy-installer` Helm chart no longer ships CRDs in its `crds/` directory. CRD lifecycle is now fully managed by the Cozystack operator via the `--install-crds` flag, which applies embedded CRD manifests on every startup using server-side apply. The platform PackageSource is also created by the operator instead of a Helm template. This ensures CRDs and the PackageSource are always up to date after each operator restart, eliminating stale CRDs from Helm's install-only behavior ([**@lexfrei**](https://github.com/lexfrei) in #2074).
## Fixes
* **[kubevirt] Update KubeVirt to v1.6.4 and CDI to v1.64.0, fix VM pod initialization**: Updated KubeVirt operator to v1.6.4 and CDI operator to v1.64.0, including live migration of existing VMs during the upgrade. Additionally, disabled serial console logging globally via the KubeVirt CR to prevent a known v1.6.x issue ([upstream #15989](https://github.com/kubevirt/kubevirt/issues/15989)) where the `guest-console-log` init container blocked virt-launcher pods from starting, causing all VMs to get stuck in `PodInitializing` state ([**@nbykov0**](https://github.com/nbykov0) in #1833; [**@kvaps**](https://github.com/kvaps) in 7dfb819).
* **[linstor] Fix DRBD+LUKS+STORAGE resource creation failure**: All newly created encrypted volumes were failing because the DRBD `.res` file was never written due to a missing `setExists(true)` call in the `LuksLayer`. Applied the upstream `skip-adjust-when-device-inaccessible` patch ([LINBIT/linstor-server#477](https://github.com/LINBIT/linstor-server/pull/477)) which fixes the root cause and also prevents unnecessary lsblk calls when devices are not yet physically present ([**@kvaps**](https://github.com/kvaps) in #2072).
* **[system] Fix monitoring-agents FQDN resolution for tenant workload clusters**: Monitoring agents (`vmagent`, `fluent-bit`) in tenant workload clusters were failing to deliver metrics and logs because service addresses used short DNS names without the cluster domain suffix. Fixed by appending the configured cluster domain from `_cluster.cluster-domain` (with fallback to `cluster.local`) to all vmagent remoteWrite URLs and fluent-bit output hosts ([**@IvanHunters**](https://github.com/IvanHunters) in #2075).
* **[cozystack-basics] Preserve existing HelmRelease values during reconciliations**: Fixed a data-loss bug where changes made to the `tenant-root` HelmRelease were silently dropped on the next forced or upgrade reconciliation of the `cozystack-basics` HelmRelease. The reconciler now merges new configuration with existing values instead of overwriting them ([**@sircthulhu**](https://github.com/sircthulhu) in #2068).
* **[cozystack-basics] Deny resourcequotas deletion for tenant admin**: Fixed the `cozy:tenant:admin:base` ClusterRole to explicitly deny deletion of `ResourceQuota` objects for tenant admins and superadmins, preventing accidental removal of tenant resource limits ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2076).
## Breaking Changes & Upgrade Notes
* **[platform] Certificate issuer configuration parameters renamed**: The `publishing.certificates.issuerType` field is renamed to `publishing.certificates.solver`, and the value `cloudflare` is renamed to `dns01` to align with standard ACME terminology. A new `publishing.certificates.issuerName` field (default: `letsencrypt-prod`) is introduced to allow pointing all ingresses at a custom ClusterIssuer. Migration 32 is included and automatically converts existing configurations during upgrade — no manual action is required ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2077).
## Documentation
* **[website] Migrate ConfigMap references to Platform Package in v1 documentation**: Updated the entire v1 documentation tree to replace legacy ConfigMap-based configuration references with the new Platform Package API, ensuring guides are consistent with the v1 configuration model ([**@sircthulhu**](https://github.com/sircthulhu) in cozystack/website#426).
* **[website] Add generic Kubernetes deployment guide for v1**: Added a new installation guide covering Cozystack deployment on any generic Kubernetes cluster, expanding the set of supported deployment targets beyond provider-specific guides ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#408).
* **[website] Refactor resource planning documentation**: Improved the resource planning guide with a clearer structure and more comprehensive coverage of planning considerations for Cozystack deployments ([**@IvanStukov**](https://github.com/IvanStukov) in cozystack/website#423).
* **[website] Add ServiceAccount API access documentation and update FAQ**: Added a new article documenting ServiceAccount API access token configuration and updated the FAQ to include related troubleshooting guidance ([**@IvanStukov**](https://github.com/IvanStukov) in cozystack/website#421).
* **[website] Update networking-mesh allowed-location-ips example**: Replaced provider-specific CLI usage with standard `kubectl` commands in the multi-location networking guide's `allowed-location-ips` example, making the documentation more universally applicable ([**@kvaps**](https://github.com/kvaps) in cozystack/website#425).
## Contributors
We'd like to thank all contributors who made this release possible:
* [**@IvanHunters**](https://github.com/IvanHunters)
* [**@IvanStukov**](https://github.com/IvanStukov)
* [**@kvaps**](https://github.com/kvaps)
* [**@lexfrei**](https://github.com/lexfrei)
* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil)
* [**@nbykov0**](https://github.com/nbykov0)
* [**@sircthulhu**](https://github.com/sircthulhu)
### New Contributors
We're excited to welcome our first-time contributors:
* [**@myasnikovdaniil**](https://github.com/myasnikovdaniil) - First contribution!
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.0-beta.6...v1.0.0-rc.1

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:39f626c802dd84f95720ffb54fcd80dfb8a58ac280498870d0a1aa30d4252f94
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:19ee4c76f0b3b7b40b97995ca78988ad8c82f6e9c75288d8b7b4b88a64f75d50

View File

@@ -1,5 +1,4 @@
{{- $targetTenant := .Values._namespace.monitoring }}
{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }}
{{- if .Values.addons.monitoringAgents.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
@@ -50,7 +49,7 @@ spec:
cluster: {{ .Release.Name }}
tenant: {{ .Release.Namespace }}
remoteWrite:
url: http://vminsert-shortterm.{{ $targetTenant }}.svc.{{ $clusterDomain }}:8480/insert/0/prometheus
url: http://vminsert-shortterm.{{ $targetTenant }}.svc:8480/insert/0/prometheus
fluent-bit:
readinessProbe:
httpGet:
@@ -73,7 +72,7 @@ spec:
[OUTPUT]
Name http
Match kube.*
Host vlogs-generic.{{ $targetTenant }}.svc.{{ $clusterDomain }}
Host vlogs-generic.{{ $targetTenant }}.svc
port 9428
compress gzip
uri /insert/jsonline?_stream_fields=stream,kubernetes_pod_name,kubernetes_container_name,kubernetes_namespace_name&_msg_field=log&_time_field=date

View File

@@ -1,9 +1,9 @@
cozystackOperator:
# Deployment variant: talos, generic, hosted
variant: talos
image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0-rc.1@sha256:5c0148116b2ab425106f6b86bbc1dfec593a83c993947c24eae92946d1c6116a
image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0-beta.6@sha256:c7490da9c1ccb51bff4dd5657ca6a33a29ac71ad9861dfa8c72fdfc8b5765b93
platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages'
platformSourceRef: 'digest=sha256:b4ee831911b9c259a073f00390559f0bd5d8c78e22e48427a64ef05ed90ca008'
platformSourceRef: 'digest=sha256:b29b87d1a2b80452ffd4db7516a102c30c55121552dcdb237055d4124d12c55d'
# Generic variant configuration (only used when cozystackOperator.variant=generic)
cozystack:
# Kubernetes API server host (IP only, no protocol/port)

View File

@@ -1,19 +0,0 @@
---
apiVersion: cozystack.io/v1alpha1
kind: PackageSource
metadata:
name: cozystack.cozystack-scheduler
spec:
sourceRef:
kind: OCIRepository
name: cozystack-packages
namespace: cozy-system
path: /
variants:
- name: default
components:
- name: cozystack-scheduler
path: system/cozystack-scheduler
install:
namespace: kube-system
releaseName: cozystack-scheduler

View File

@@ -155,6 +155,5 @@
{{include "cozystack.platform.package.default" (list "cozystack.bootbox" $) }}
{{- end }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.hetzner-robotlb" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.cozystack-scheduler" $) }}
{{- end }}

View File

@@ -5,7 +5,7 @@ sourceRef:
path: /
migrations:
enabled: false
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.0.0-rc.1@sha256:21a09c9f8dfd0a0c9b8c14c70029a39bfce021c66f1d4cacad9764c35dce6e8f
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.0.0-beta.6@sha256:37c78dafcedbdad94acd9912550db0b4875897150666b8a06edfa894de99064e
targetVersion: 33
# Bundle deployment configuration
bundles:
@@ -46,7 +46,7 @@ publishing:
apiServerEndpoint: "" # example: "https://api.example.org"
externalIPs: []
certificates:
solver: http01 # "http01" or "dns01"
solver: http01 # "http01" or "dns01"
issuerName: letsencrypt-prod
# Authentication configuration
authentication:

View File

@@ -1,2 +1,2 @@
e2e:
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0-rc.1@sha256:0eae9f519669667d60b160ebb93c127843c470ad9ca3447fceaa54604503a7ba
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0-beta.6@sha256:09af5901abcbed2b612d2d93c163e8ad3948bc55a1d8beae714b4fb2b8f7d91d

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/matchbox:v1.0.0-rc.1@sha256:3306de19f1ad49a02c735d16b82d7c2ec015c8e0563f120f216274e9a3804431
ghcr.io/cozystack/cozystack/matchbox:v1.0.0-beta.6@sha256:212f624957447f5a932fd5d4564eb8c97694d336b7dc877a2833c1513c0d074d

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-rc.1@sha256:235b194a531b70e266a10ef78d2955d19f5b659513f23d8b3cfbbc0dff7fc1c0
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-beta.6@sha256:235b194a531b70e266a10ef78d2955d19f5b659513f23d8b3cfbbc0dff7fc1c0

View File

@@ -1,5 +1,5 @@
backupController:
image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0-rc.1@sha256:0bb4173bdcd3d917a7bd358ecc2c6a053a06ab0bd1fcdb89d1016a66173e6dfb"
image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0-beta.6@sha256:365214a74ffc34a9314a62a7d4b491590051fc5486f6bae9913c0c1289983d43"
replicas: 2
debug: false
metrics:

View File

@@ -1,5 +1,5 @@
backupStrategyController:
image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0-rc.1@sha256:c2d975574ea9edcd785b533e01add37909959a64ef815529162dfe1f472ea702"
image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0-beta.6@sha256:aa04ee61dce11950162606fc8db2d5cbc6f5b32ba700f790b3f1eee10d65efb1"
replicas: 2
debug: false
metrics:

View File

@@ -1,3 +1,3 @@
cozystackAPI:
image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-rc.1@sha256:9195ac6ef1d29aba3e1903f29a9aeeb72f5bf7ba3dbd7ebd866a06a4433e5915
image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-beta.6@sha256:d89cf68fb622d0dbef7db98db09d352efc93c2cce448d11f2d73dcd363e911b7
replicas: 2

View File

@@ -1,4 +1,4 @@
cozystackController:
image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0-rc.1@sha256:0ac4b6d55daf79e2a7a045cd21b48ba598ac2628442fd9c4d0556cf9af6b83be
image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0-beta.6@sha256:d55a3c288934b1f69a00321bc8a94776915556b5f882fe6ac615e9de2701c61f
debug: false
disableTelemetry: false

View File

@@ -1,3 +0,0 @@
apiVersion: v2
name: cozy-cozystack-scheduler
version: 0.1.0

View File

@@ -1,10 +0,0 @@
export NAME=cozystack-scheduler
export NAMESPACE=kube-system
include ../../../hack/package.mk
update:
rm -rf crds templates values.yaml Chart.yaml
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/cozystack/cozystack-scheduler | awk -F'[/^]' 'END{print $$3}') && \
curl -sSL https://github.com/cozystack/cozystack-scheduler/archive/refs/tags/$${tag}.tar.gz | \
tar xzvf - --strip 2 cozystack-scheduler-$${tag#*v}/chart

View File

@@ -1,9 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cozystack-scheduler
rules:
- apiGroups: ["cozystack.io"]
resources:
- schedulingclasses
verbs: ["get", "list", "watch"]

View File

@@ -1,38 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cozystack-scheduler:kube-scheduler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:kube-scheduler
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cozystack-scheduler:volume-scheduler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:volume-scheduler
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cozystack-scheduler
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cozystack-scheduler
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}

View File

@@ -1,54 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cozystack-scheduler-config
namespace: {{ .Release.Namespace }}
data:
scheduler-config.yaml: |
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
leaderElection:
leaderElect: true
resourceNamespace: {{ .Release.Namespace }}
resourceName: cozystack-scheduler
profiles:
- schedulerName: cozystack-scheduler
plugins:
preFilter:
disabled:
- name: InterPodAffinity
- name: NodeAffinity
- name: PodTopologySpread
enabled:
- name: CozystackInterPodAffinity
- name: CozystackNodeAffinity
- name: CozystackPodTopologySpread
- name: CozystackSchedulingClass
filter:
disabled:
- name: InterPodAffinity
- name: NodeAffinity
- name: PodTopologySpread
enabled:
- name: CozystackInterPodAffinity
- name: CozystackNodeAffinity
- name: CozystackPodTopologySpread
- name: CozystackSchedulingClass
preScore:
disabled:
- name: InterPodAffinity
- name: NodeAffinity
- name: PodTopologySpread
enabled:
- name: CozystackInterPodAffinity
- name: CozystackNodeAffinity
- name: CozystackPodTopologySpread
score:
disabled:
- name: InterPodAffinity
- name: NodeAffinity
- name: PodTopologySpread
enabled:
- name: CozystackInterPodAffinity
- name: CozystackNodeAffinity
- name: CozystackPodTopologySpread

View File

@@ -1,37 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
app: cozystack-scheduler
template:
metadata:
labels:
app: cozystack-scheduler
spec:
serviceAccountName: cozystack-scheduler
containers:
- name: cozystack-scheduler
image: {{ .Values.image }}
command:
- /cozystack-scheduler
- --config=/etc/kubernetes/scheduler-config.yaml
livenessProbe:
httpGet:
path: /healthz
port: 10259
scheme: HTTPS
initialDelaySeconds: 15
volumeMounts:
- name: config
mountPath: /etc/kubernetes/scheduler-config.yaml
subPath: scheduler-config.yaml
readOnly: true
volumes:
- name: config
configMap:
name: cozystack-scheduler-config

View File

@@ -1,40 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cozystack-scheduler:extension-apiserver-authentication-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cozystack-scheduler:leader-election
namespace: {{ .Release.Namespace }}
rules:
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["create", "get", "list", "update", "watch"]
- apiGroups: ["coordination.k8s.io"]
resources: ["leasecandidates"]
verbs: ["create", "get", "list", "update", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cozystack-scheduler:leader-election
namespace: {{ .Release.Namespace }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: cozystack-scheduler:leader-election
subjects:
- kind: ServiceAccount
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}

View File

@@ -1,5 +0,0 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: cozystack-scheduler
namespace: {{ .Release.Namespace }}

View File

@@ -1,2 +0,0 @@
image: ghcr.io/cozystack/cozystack/cozystack-scheduler:v0.1.0@sha256:5f7150c82177478467ff80628acb5a400291aff503364aa9e26fc346d79a73cf
replicas: 1

View File

@@ -1,6 +1,6 @@
{{- $brandingConfig := .Values._cluster.branding | default dict }}
{{- $tenantText := "v1.0.0-rc.1" }}
{{- $tenantText := "v1.0.0-beta.6" }}
{{- $footerText := "Cozystack" }}
{{- $titleText := "Cozystack Dashboard" }}
{{- $logoText := "" }}

View File

@@ -1,6 +1,6 @@
openapiUI:
image: ghcr.io/cozystack/cozystack/openapi-ui:v1.0.0-rc.1@sha256:26e787b259ab8722d61f89e07eda200ef2180ed10b0df8596d75bba15526feb0
image: ghcr.io/cozystack/cozystack/openapi-ui:v1.0.0-beta.6@sha256:c333637673a9e878f6c4ed0fc96db55967bbcf94b2434d075b0f0c6fcfcf9eff
openapiUIK8sBff:
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.0.0-rc.1@sha256:0f508427bfa5a650eda6c5ef01ea32a586ac485a54902d7649ec49cc84f676f7
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.0.0-beta.6@sha256:1b3ea6d4c7dbbe6a8def3b2807fffdfab2ac4afc39d7a846e57dd491fa168f92
tokenProxy:
image: ghcr.io/cozystack/cozystack/token-proxy:v1.0.0-rc.1@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc
image: ghcr.io/cozystack/cozystack/token-proxy:v1.0.0-beta.6@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/grafana-dashboards:v1.0.0-rc.1@sha256:7a3c9af59f8d74d5a23750bbc845c7de64610dbd4d4f84011e10be037b3ce2a0
ghcr.io/cozystack/cozystack/grafana-dashboards:v1.0.0-beta.6@sha256:7a3c9af59f8d74d5a23750bbc845c7de64610dbd4d4f84011e10be037b3ce2a0

View File

@@ -3,7 +3,7 @@ kamaji:
deploy: false
image:
pullPolicy: IfNotPresent
tag: v1.0.0-rc.1@sha256:0ca47c0d72a198f52f029bea30e89557680eac65c7914369b092d8ed9cc9997b
tag: v1.0.0-beta.6@sha256:05f8e166dc94aadb74cd6448f6f96fbd2167057a559097516a41b8f53e434918
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:v1.0.0-rc.1@sha256:0ca47c0d72a198f52f029bea30e89557680eac65c7914369b092d8ed9cc9997b
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.0.0-beta.6@sha256:05f8e166dc94aadb74cd6448f6f96fbd2167057a559097516a41b8f53e434918

View File

@@ -1,4 +1,4 @@
portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.0.0-rc.1@sha256:6be9aa4b2dda15bf7300bd961cbc71c8bbf9ce97bc3cf613ef5a012d329b4e70
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.0.0-beta.6@sha256:8e964605efe54e73a94c84abec7dbb5a011c02ccece282bef8ae7b70fce3d217
ovnCentralName: ovn-central

View File

@@ -1,3 +1,3 @@
portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.0.0-rc.1@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.0.0-beta.6@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a

View File

@@ -1,5 +1,5 @@
lineageControllerWebhook:
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.0.0-rc.1@sha256:473ed28fcd7ddc35319a0fd33dd0fff3e56491b572677545a6e317b53578c53d
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.0.0-beta.6@sha256:cf577e56ebc2b94205741d9c5b08f2983cec0811f0c2890edca8fdca22624de1
debug: false
localK8sAPIEndpoint:
enabled: true

View File

@@ -1,7 +1,7 @@
piraeusServer:
image:
repository: ghcr.io/cozystack/cozystack/piraeus-server
tag: 1.32.3@sha256:59806529a090cb42e2fa2696e09814282b80e76a299b5fe9feec46772edd6876
tag: 1.32.3@sha256:66eadfc98cd809d2b3c4e6fd631bcd0c4b4cd72a7fb819ac4a0cab7904280546
# Talos-specific workarounds (disable for generic Linux like Ubuntu/Debian)
talos:
enabled: true

View File

@@ -280,8 +280,8 @@ vmagent:
cluster: cozystack
remoteWrite:
urls:
- http://vminsert-shortterm.{{ .Values.global.target }}.svc:8480/insert/0/prometheus
- http://vminsert-longterm.{{ .Values.global.target }}.svc:8480/insert/0/prometheus
- http://vminsert-shortterm.{{ .Values.global.target }}.svc.{{ (index .Values._cluster "cluster-domain") | default "cluster.local" }}:8480/insert/0/prometheus
- http://vminsert-longterm.{{ .Values.global.target }}.svc.{{ (index .Values._cluster "cluster-domain") | default "cluster.local" }}:8480/insert/0/prometheus
extraArgs: {}
fluent-bit:
@@ -344,7 +344,7 @@ fluent-bit:
[OUTPUT]
Name http
Match kube.*
Host vlogs-generic.{{ .Values.global.target }}.svc
Host vlogs-generic.{{ .Values.global.target }}.svc.{{ (index .Values._cluster "cluster-domain") | default "cluster.local" }}
port 9428
compress gzip
uri /insert/jsonline?_stream_fields=log_source,stream,kubernetes_pod_name,kubernetes_container_name,kubernetes_namespace_name&_msg_field=log&_time_field=date
@@ -355,7 +355,7 @@ fluent-bit:
[OUTPUT]
Name http
Match events.*
Host vlogs-generic.{{ .Values.global.target }}.svc
Host vlogs-generic.{{ .Values.global.target }}.svc.{{ (index .Values._cluster "cluster-domain") | default "cluster.local" }}
port 9428
compress gzip
uri /insert/jsonline?_stream_fields=log_source,reason,meatdata_namespace,metadata_name&_msg_field=message&_time_field=date
@@ -366,7 +366,7 @@ fluent-bit:
[OUTPUT]
Name http
Match audit.*
Host vlogs-generic.{{ .Values.global.target }}.svc
Host vlogs-generic.{{ .Values.global.target }}.svc.{{ (index .Values._cluster "cluster-domain") | default "cluster.local" }}
port 9428
compress gzip
uri /insert/jsonline?_stream_fields=log_source,stage,user_username,verb,requestUri&_msg_field=requestURI&_time_field=date

View File

@@ -1,3 +1,3 @@
objectstorage:
controller:
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.0.0-rc.1@sha256:b4c972769afda76c48b58e7acf0ac66a0abf16a622f245c60338f432872f640a"
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.0.0-beta.6@sha256:b4c972769afda76c48b58e7acf0ac66a0abf16a622f245c60338f432872f640a"

View File

@@ -177,7 +177,7 @@ seaweedfs:
bucketClassName: "seaweedfs"
region: ""
sidecar:
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-rc.1@sha256:235b194a531b70e266a10ef78d2955d19f5b659513f23d8b3cfbbc0dff7fc1c0"
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-beta.6@sha256:235b194a531b70e266a10ef78d2955d19f5b659513f23d8b3cfbbc0dff7fc1c0"
certificates:
commonName: "SeaweedFS CA"
ipAddresses: []