From ca9ab6833142de63a59ce70df918baf2daa6226f Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Mon, 8 Dec 2025 10:02:57 -0800 Subject: [PATCH 01/11] Stop using kots --- .github/workflows/ci.yaml | 33 +- .github/workflows/dependencies.yaml | 2 +- README.md | 32 +- api/controllers/app/controller.go | 12 +- api/controllers/app/controller_mock.go | 4 +- api/controllers/app/install.go | 26 +- api/controllers/app/tests/test_suite.go | 106 +- .../kubernetes/install/controller.go | 1 + .../kubernetes/install/controller_mock.go | 4 +- .../kubernetes/upgrade/controller.go | 9 + api/controllers/linux/install/controller.go | 1 + .../linux/install/controller_mock.go | 4 +- .../linux/install/controller_test.go | 4 +- api/controllers/linux/upgrade/controller.go | 1 + .../linux/upgrade/controller_test.go | 4 +- api/docs/docs.go | 2 +- api/docs/swagger.json | 2 +- api/docs/swagger.yaml | 18 + .../handlers/kubernetes/install/handler.go | 10 +- .../handlers/kubernetes/kubernetes.go | 1 + .../handlers/linux/install/handler.go | 14 +- api/internal/managers/airgap/process.go | 6 +- api/internal/managers/app/install/install.go | 209 +- .../managers/app/install/install_test.go | 874 +++---- api/internal/managers/app/install/manager.go | 22 +- api/internal/managers/app/install/mock.go | 11 +- .../app/install/namespaces_reconciler.go | 228 ++ .../app/install/namespaces_reconciler_test.go | 447 ++++ api/internal/managers/app/install/status.go | 19 +- api/internal/managers/app/install/util.go | 61 +- .../managers/app/install/util_test.go | 58 +- api/internal/managers/app/release/manager.go | 1 + .../managers/app/release/manager_mock.go | 9 + api/internal/managers/app/release/template.go | 56 +- .../managers/app/release/template_test.go | 758 +++++- api/internal/managers/app/release/util.go | 2 +- .../managers/linux/infra/image_test.go | 16 +- api/internal/managers/linux/infra/upgrade.go | 6 +- .../managers/linux/installation/config.go | 89 +- .../linux/installation/config_test.go | 185 +- api/internal/store/app/install/store.go | 41 + api/internal/store/app/install/store_mock.go | 12 + api/internal/store/app/install/store_test.go | 102 + api/pkg/template/registry.go | 6 +- api/pkg/template/registry_test.go | 30 +- api/types/app.go | 20 +- api/types/registry.go | 20 +- cmd/buildtools/utils.go | 5 +- .../local-artifact-mirror/Dockerfile.ttlsh | 2 +- dev/dockerfiles/operator/Dockerfile.local | 2 +- dev/dockerfiles/operator/Dockerfile.ttlsh | 2 +- .../nginx-app-helm-v1beta2.yaml | 8 +- go.mod | 114 +- go.sum | 267 +-- kinds/go.mod | 28 +- kinds/go.sum | 75 +- operator/pkg/cli/upgrade_job.go | 1 + .../integration/hostcabundle_test.go | 5 +- .../integration/kubernetes_test.go | 5 +- .../adminconsole/integration/linux_test.go | 5 +- pkg/addons/adminconsole/static/metadata.yaml | 18 +- pkg/addons/adminconsole/values.go | 3 +- pkg/addons/adminconsole/values_test.go | 3 +- .../integration/hostcabundle_test.go | 5 +- pkg/addons/openebs/static/metadata.yaml | 15 +- .../velero/integration/hostcabundle_test.go | 5 +- .../velero/integration/k0ssubdir_test.go | 5 +- pkg/addons/velero/static/metadata.yaml | 4 +- pkg/helm/binary_executor.go | 11 +- pkg/helm/binary_executor_test.go | 26 +- pkg/helm/client.go | 709 +++--- pkg/helm/client_test.go | 1303 ++++++++++- pkg/helm/interface.go | 8 +- pkg/helm/mock_client.go | 22 +- tests/dryrun/Dockerfile | 2 +- tests/dryrun/v3_install_test.go | 51 +- utils/go.mod | 2 +- versions.mk | 4 +- web/package-lock.json | 2075 +++++------------ web/package.json | 16 +- web/src/types/api.ts | 7 + 81 files changed, 5277 insertions(+), 3114 deletions(-) create mode 100644 api/internal/managers/app/install/namespaces_reconciler.go create mode 100644 api/internal/managers/app/install/namespaces_reconciler_test.go diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1d17740911..266ab75b00 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -267,8 +267,31 @@ jobs: with: go-version-file: go.mod cache-dependency-path: "**/*.sum" - - name: Free up runner disk space - run: *free-disk-space + - name: Free up runner disk space # this is much faster than .github/actions/free-disk-space + run: | + df -h + sudo rm -rf \ + /usr/share/swift \ + /usr/share/dotnet \ + /usr/lib/jvm \ + /usr/local/share/boost \ + /usr/local/lib/heroku \ + /usr/local/julia* \ + /usr/local/.ghcup \ + /usr/local/share/powershell \ + /usr/local/bin/aliyun \ + /usr/local/bin/azcopy \ + /usr/local/bin/bicep \ + /usr/local/bin/cpack \ + /usr/local/bin/hub \ + /usr/local/bin/minikube \ + /usr/local/bin/packer \ + /usr/local/bin/pulumi* \ + /usr/local/bin/sam \ + /usr/local/bin/stack \ + /usr/local/bin/terraform \ + /usr/local/bin/oc + df -h - name: Install kind uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab with: @@ -284,8 +307,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - - name: Free up runner disk space - run: *free-disk-space - name: Go cache uses: actions/cache@v4 with: @@ -435,7 +456,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Free up runner disk space + - name: Free up runner disk space # this is much faster than .github/actions/free-disk-space run: *free-disk-space - name: Cache embedded bins @@ -591,7 +612,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Free up runner disk space + - name: Free up runner disk space # this is much faster than .github/actions/free-disk-space run: *free-disk-space - name: Cache embedded bins diff --git a/.github/workflows/dependencies.yaml b/.github/workflows/dependencies.yaml index e8a6db0e99..0704de4148 100644 --- a/.github/workflows/dependencies.yaml +++ b/.github/workflows/dependencies.yaml @@ -45,7 +45,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - version=$(gh release list --repo helm/helm --json tagName,createdAt | jq -r '.[] | select(.tagName | startswith("v3.")) | .tagName' | head -1) + version=$(gh release list --repo helm/helm --json tagName,isLatest | jq -r '.[] | select(.isLatest) | .tagName') echo "helm version: $version" sed -i "/^HELM_VERSION/c\HELM_VERSION = $version" versions.mk diff --git a/README.md b/README.md index cfb8ede70d..6aa3ef7af0 100644 --- a/README.md +++ b/README.md @@ -476,32 +476,32 @@ dagger call with-one-password --service-account=env:OP_SERVICE_ACCOUNT_TOKEN \ ## Releasing -Embedded Cluster maintains support for the current and two previous k8s minor versions, ensuring backward compatibility while supporting the latest features. -All supported versions are released simultaneously from the main branch using a structured tagging approach that combines the application version with the supported k8s version. +Embedded Cluster maintains support for the current and two previous K0s minor versions, ensuring backward compatibility while supporting the latest features. +All supported versions are released simultaneously from the main branch using a structured tagging approach that combines the application version with the supported K0s version. ### Release Tagging Strategy -Releases follow the format: `{APP_VERSION}+k8s-{K0S_MINOR_VERSION}` +Releases follow the format: `{APP_VERSION}+k0s-{K0S_MINOR_VERSION}` **Examples:** -- `2.10.0+k8s-1.33` - Application version 2.10.0 with k8s 1.33.x support -- `2.10.0+k8s-1.32` - Application version 2.10.0 with k8s 1.32.x support -- `2.10.0+k8s-1.31` - Application version 2.10.0 with k8s 1.31.x support +- `2.10.0+k0s-1.33` - Application version 2.10.0 with K0s 1.33.x support +- `2.10.0+k0s-1.32` - Application version 2.10.0 with K0s 1.32.x support +- `2.10.0+k0s-1.31` - Application version 2.10.0 with K0s 1.31.x support ### Release Process 1. **Prepare the release commit** - Ensure all changes are committed and tested -2. **Create annotated tags** - Tag the same commit with all supported k8s minor versions using annotated tags with descriptive messages: +2. **Create annotated tags** - Tag the same commit with all supported K0s minor versions using annotated tags with descriptive messages: ```bash - # Tag for k8s 1.33.x support - git tag -a 2.10.0+k8s-1.33 -m "Release 2.10.0+k8s-1.33" - git push origin 2.10.0+k8s-1.33 + # Tag for K0s 1.33.x support + git tag -a 2.10.0+k0s-1.33 -m "Release 2.10.0+k0s-1.33" + git push origin 2.10.0+k0s-1.33 - # Tag for k8s 1.32.x support - git tag -a 2.10.0+k8s-1.32 -m "Release 2.10.0+k8s-1.32" - git push origin 2.10.0+k8s-1.32 + # Tag for K0s 1.32.x support + git tag -a 2.10.0+k0s-1.32 -m "Release 2.10.0+k0s-1.32" + git push origin 2.10.0+k0s-1.32 - # Tag for k8s 1.31.x support - git tag -a 2.10.0+k8s-1.31 -m "Release 2.10.0+k8s-1.31" - git push origin 2.10.0+k8s-1.31 + # Tag for K0s 1.31.x support + git tag -a 2.10.0+k0s-1.31 -m "Release 2.10.0+k0s-1.31" + git push origin 2.10.0+k0s-1.31 ``` diff --git a/api/controllers/app/controller.go b/api/controllers/app/controller.go index 258b43be76..970525a3dc 100644 --- a/api/controllers/app/controller.go +++ b/api/controllers/app/controller.go @@ -20,6 +20,7 @@ import ( kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" "github.com/sirupsen/logrus" helmcli "helm.sh/helm/v3/pkg/cli" + "k8s.io/client-go/metadata" "sigs.k8s.io/controller-runtime/pkg/client" kyaml "sigs.k8s.io/yaml" ) @@ -32,7 +33,7 @@ type Controller interface { GetAppPreflightStatus(ctx context.Context) (types.Status, error) GetAppPreflightOutput(ctx context.Context) (*types.PreflightsOutput, error) GetAppPreflightTitles(ctx context.Context) ([]string, error) - InstallApp(ctx context.Context, ignoreAppPreflights bool) error + InstallApp(ctx context.Context, opts InstallAppOptions) error GetAppInstallStatus(ctx context.Context) (types.AppInstall, error) UpgradeApp(ctx context.Context, ignoreAppPreflights bool) error GetAppUpgradeStatus(ctx context.Context) (types.AppUpgrade, error) @@ -52,6 +53,7 @@ type AppController struct { releaseData *release.ReleaseData hcli helm.Client kcli client.Client + mcli metadata.Interface preflightRunner preflights.PreflightRunnerInterface kubernetesEnvSettings *helmcli.EnvSettings store store.Store @@ -129,6 +131,12 @@ func WithKubeClient(kcli client.Client) AppControllerOption { } } +func WithMetadataClient(mcli metadata.Interface) AppControllerOption { + return func(c *AppController) { + c.mcli = mcli + } +} + func WithKubernetesEnvSettings(envSettings *helmcli.EnvSettings) AppControllerOption { return func(c *AppController) { c.kubernetesEnvSettings = envSettings @@ -262,7 +270,9 @@ func NewAppController(opts ...AppControllerOption) (*AppController, error) { appinstallmanager.WithAirgapBundle(controller.airgapBundle), appinstallmanager.WithAppInstallStore(controller.store.AppInstallStore()), appinstallmanager.WithKubeClient(controller.kcli), + appinstallmanager.WithMetadataClient(controller.mcli), appinstallmanager.WithKubernetesEnvSettings(controller.kubernetesEnvSettings), + appinstallmanager.WithHelmClient(controller.hcli), ) if err != nil { return nil, fmt.Errorf("create app install manager: %w", err) diff --git a/api/controllers/app/controller_mock.go b/api/controllers/app/controller_mock.go index 8544b58f35..11d1f9ef11 100644 --- a/api/controllers/app/controller_mock.go +++ b/api/controllers/app/controller_mock.go @@ -69,8 +69,8 @@ func (m *MockController) GetAppPreflightTitles(ctx context.Context) ([]string, e } // InstallApp mocks the InstallApp method -func (m *MockController) InstallApp(ctx context.Context, ignoreAppPreflights bool) error { - args := m.Called(ctx, ignoreAppPreflights) +func (m *MockController) InstallApp(ctx context.Context, opts InstallAppOptions) error { + args := m.Called(ctx, opts) return args.Error(0) } diff --git a/api/controllers/app/install.go b/api/controllers/app/install.go index cd708c82c9..5ab27d256d 100644 --- a/api/controllers/app/install.go +++ b/api/controllers/app/install.go @@ -9,14 +9,22 @@ import ( "github.com/replicatedhq/embedded-cluster/api/internal/states" "github.com/replicatedhq/embedded-cluster/api/types" + ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" ) var ( ErrAppPreflightChecksFailed = errors.New("app preflight checks failed") ) +type InstallAppOptions struct { + IgnoreAppPreflights bool + ProxySpec *ecv1beta1.ProxySpec + RegistrySettings *types.RegistrySettings + HostCABundlePath string +} + // InstallApp triggers app installation with proper state transitions and panic handling -func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool) (finalErr error) { +func (c *AppController) InstallApp(ctx context.Context, opts InstallAppOptions) (finalErr error) { logger := c.logger.WithField("operation", "install-app") lock, err := c.stateMachine.AcquireLock() @@ -45,7 +53,7 @@ func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool } allowIgnoreAppPreflights := true // TODO: implement if we decide to support a ignore-app-preflights CLI flag for V3 - if !ignoreAppPreflights || !allowIgnoreAppPreflights { + if !opts.IgnoreAppPreflights || !allowIgnoreAppPreflights { return types.NewBadRequestError(ErrAppPreflightChecksFailed) } @@ -60,9 +68,15 @@ func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool } // Get config values for app installation - configValues, err := c.appConfigManager.GetKotsadmConfigValues() + appConfigValues, err := c.GetAppConfigValues(ctx) + if err != nil { + return fmt.Errorf("get app config values for app install: %w", err) + } + + // Extract installable Helm charts from release manager + installableCharts, err := c.appReleaseManager.ExtractInstallableHelmCharts(ctx, appConfigValues, opts.ProxySpec, opts.RegistrySettings) if err != nil { - return fmt.Errorf("get kotsadm config values for app install: %w", err) + return fmt.Errorf("extract installable helm charts: %w", err) } err = c.stateMachine.Transition(lock, states.StateAppInstalling, nil) @@ -97,8 +111,8 @@ func (c *AppController) InstallApp(ctx context.Context, ignoreAppPreflights bool return fmt.Errorf("set status to running: %w", err) } - // Install the app - err := c.appInstallManager.Install(ctx, configValues) + // Install the app with installable charts + err = c.appInstallManager.Install(ctx, installableCharts, opts.RegistrySettings, opts.HostCABundlePath) if err != nil { return fmt.Errorf("install app: %w", err) } diff --git a/api/controllers/app/tests/test_suite.go b/api/controllers/app/tests/test_suite.go index 4983156a98..bf3c3099cc 100644 --- a/api/controllers/app/tests/test_suite.go +++ b/api/controllers/app/tests/test_suite.go @@ -16,6 +16,8 @@ import ( "github.com/replicatedhq/embedded-cluster/api/internal/states" "github.com/replicatedhq/embedded-cluster/api/internal/store" "github.com/replicatedhq/embedded-cluster/api/types" + ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" + "github.com/replicatedhq/embedded-cluster/pkg/helm" "github.com/replicatedhq/embedded-cluster/pkg/release" kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" @@ -468,16 +470,18 @@ func (s *AppControllerTestSuite) TestInstallApp() { tests := []struct { name string ignoreAppPreflights bool + proxySpec *ecv1beta1.ProxySpec + registrySettings *types.RegistrySettings currentState statemachine.State expectedState statemachine.State - setupMocks func(*appconfig.MockAppConfigManager, *appinstallmanager.MockAppInstallManager, *apppreflightmanager.MockAppPreflightManager, *store.MockStore) + setupMocks func(*appconfig.MockAppConfigManager, *appreleasemanager.MockAppReleaseManager, *appinstallmanager.MockAppInstallManager, *apppreflightmanager.MockAppPreflightManager, *store.MockStore) expectedErr bool }{ { name: "invalid state transition from succeeded state", currentState: states.StateSucceeded, expectedState: states.StateSucceeded, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { // No mocks needed for invalid state transition }, expectedErr: true, @@ -486,7 +490,7 @@ func (s *AppControllerTestSuite) TestInstallApp() { name: "invalid state transition from infrastructure installing state", currentState: states.StateInfrastructureInstalling, expectedState: states.StateInfrastructureInstalling, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { // No mocks needed for invalid state transition }, expectedErr: true, @@ -495,23 +499,25 @@ func (s *AppControllerTestSuite) TestInstallApp() { name: "successful app installation from app preflights succeeded state", currentState: states.StateAppPreflightsSucceeded, expectedState: states.StateSucceeded, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + expectedCharts := []types.InstallableHelmChart{ + { + Archive: []byte("chart-archive-data"), + Values: map[string]any{"key": "value"}, + }, + } + appConfigValues := types.AppConfigValues{ + "test-key": types.AppConfigValue{Value: "test-value"}, + } mock.InOrder( - acm.On("GetKotsadmConfigValues").Return(kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "test-key": {Value: "test-value"}, - }, - }, - }, nil), + acm.On("GetConfigValues").Return(appConfigValues, nil), + arm.On("ExtractInstallableHelmCharts", mock.Anything, appConfigValues, mock.AnythingOfType("*v1beta1.ProxySpec"), mock.AnythingOfType("*types.RegistrySettings")).Return(expectedCharts, nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateRunning })).Return(nil), - aim.On("Install", mock.Anything, mock.MatchedBy(func(cv kotsv1beta1.ConfigValues) bool { - return cv.Spec.Values["test-key"].Value == "test-value" - }), mock.Anything).Return(nil), + aim.On("Install", mock.Anything, expectedCharts, mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateSucceeded @@ -524,23 +530,19 @@ func (s *AppControllerTestSuite) TestInstallApp() { name: "successful app installation from app preflights failed bypassed state", currentState: states.StateAppPreflightsFailedBypassed, expectedState: states.StateSucceeded, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + appConfigValues := types.AppConfigValues{ + "test-key": types.AppConfigValue{Value: "test-value"}, + } mock.InOrder( - acm.On("GetKotsadmConfigValues").Return(kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "test-key": {Value: "test-value"}, - }, - }, - }, nil), + acm.On("GetConfigValues").Return(appConfigValues, nil), + arm.On("ExtractInstallableHelmCharts", mock.Anything, appConfigValues, mock.AnythingOfType("*v1beta1.ProxySpec"), mock.AnythingOfType("*types.RegistrySettings")).Return([]types.InstallableHelmChart{}, nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateRunning })).Return(nil), - aim.On("Install", mock.Anything, mock.MatchedBy(func(cv kotsv1beta1.ConfigValues) bool { - return cv.Spec.Values["test-key"].Value == "test-value" - }), mock.Anything).Return(nil), + aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateSucceeded @@ -553,23 +555,19 @@ func (s *AppControllerTestSuite) TestInstallApp() { name: "failed app installation from app preflights succeeded state", currentState: states.StateAppPreflightsSucceeded, expectedState: states.StateAppInstallFailed, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + appConfigValues := types.AppConfigValues{ + "test-key": types.AppConfigValue{Value: "test-value"}, + } mock.InOrder( - acm.On("GetKotsadmConfigValues").Return(kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "test-key": {Value: "test-value"}, - }, - }, - }, nil), + acm.On("GetConfigValues").Return(appConfigValues, nil), + arm.On("ExtractInstallableHelmCharts", mock.Anything, appConfigValues, mock.AnythingOfType("*v1beta1.ProxySpec"), mock.AnythingOfType("*types.RegistrySettings")).Return([]types.InstallableHelmChart{}, nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateRunning })).Return(nil), - aim.On("Install", mock.Anything, mock.MatchedBy(func(cv kotsv1beta1.ConfigValues) bool { - return cv.Spec.Values["test-key"].Value == "test-value" - }), mock.Anything).Return(errors.New("install error")), + aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(errors.New("install error")), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateFailed && strings.Contains(status.Description, "install error") @@ -582,9 +580,9 @@ func (s *AppControllerTestSuite) TestInstallApp() { name: "get config values error", currentState: states.StateAppPreflightsSucceeded, expectedState: states.StateAppPreflightsSucceeded, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { mock.InOrder( - acm.On("GetKotsadmConfigValues").Return(kotsv1beta1.ConfigValues{}, errors.New("config values error")), + acm.On("GetConfigValues").Return(types.AppConfigValues{}, errors.New("config values error")), ) }, expectedErr: true, @@ -594,7 +592,10 @@ func (s *AppControllerTestSuite) TestInstallApp() { ignoreAppPreflights: true, currentState: states.StateAppPreflightsFailed, expectedState: states.StateSucceeded, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + appConfigValues := types.AppConfigValues{ + "test-key": types.AppConfigValue{Value: "test-value"}, + } mock.InOrder( // Mock GetAppPreflightOutput to return non-strict failures (can be bypassed) apm.On("GetAppPreflightOutput", mock.Anything).Return(&types.PreflightsOutput{ @@ -607,21 +608,14 @@ func (s *AppControllerTestSuite) TestInstallApp() { }, }, nil), - acm.On("GetKotsadmConfigValues").Return(kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "test-key": {Value: "test-value"}, - }, - }, - }, nil), + acm.On("GetConfigValues").Return(appConfigValues, nil), + arm.On("ExtractInstallableHelmCharts", mock.Anything, appConfigValues, mock.AnythingOfType("*v1beta1.ProxySpec"), mock.AnythingOfType("*types.RegistrySettings")).Return([]types.InstallableHelmChart{}, nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateRunning })).Return(nil), - aim.On("Install", mock.Anything, mock.MatchedBy(func(cv kotsv1beta1.ConfigValues) bool { - return cv.Spec.Values["test-key"].Value == "test-value" - }), mock.Anything).Return(nil), + aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateSucceeded @@ -635,7 +629,7 @@ func (s *AppControllerTestSuite) TestInstallApp() { ignoreAppPreflights: false, currentState: states.StateAppPreflightsFailed, expectedState: states.StateAppPreflightsFailed, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { mock.InOrder( // Mock GetAppPreflightOutput to return non-strict failures (method should be called but bypass denied) apm.On("GetAppPreflightOutput", mock.Anything).Return(&types.PreflightsOutput{ @@ -656,7 +650,7 @@ func (s *AppControllerTestSuite) TestInstallApp() { ignoreAppPreflights: true, currentState: states.StateAppPreflightsFailed, expectedState: states.StateAppPreflightsFailed, - setupMocks: func(acm *appconfig.MockAppConfigManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { + setupMocks: func(acm *appconfig.MockAppConfigManager, arm *appreleasemanager.MockAppReleaseManager, aim *appinstallmanager.MockAppInstallManager, apm *apppreflightmanager.MockAppPreflightManager, store *store.MockStore) { mock.InOrder( // Mock GetAppPreflightOutput to return strict failures (cannot be bypassed) apm.On("GetAppPreflightOutput", mock.Anything).Return(&types.PreflightsOutput{ @@ -679,6 +673,7 @@ func (s *AppControllerTestSuite) TestInstallApp() { appPreflightManager := &apppreflightmanager.MockAppPreflightManager{} appReleaseManager := &appreleasemanager.MockAppReleaseManager{} appInstallManager := &appinstallmanager.MockAppInstallManager{} + mockHelmClient := &helm.MockClient{} sm := s.CreateInstallStateMachine(tt.currentState) appConfigManager := &appconfig.MockAppConfigManager{} @@ -694,11 +689,16 @@ func (s *AppControllerTestSuite) TestInstallApp() { appcontroller.WithAppInstallManager(appInstallManager), appcontroller.WithStore(store), appcontroller.WithReleaseData(&release.ReleaseData{}), + appcontroller.WithHelmClient(mockHelmClient), ) require.NoError(t, err, "failed to create install controller") - tt.setupMocks(appConfigManager, appInstallManager, appPreflightManager, store) - err = controller.InstallApp(t.Context(), tt.ignoreAppPreflights) + tt.setupMocks(appConfigManager, appReleaseManager, appInstallManager, appPreflightManager, store) + err = controller.InstallApp(t.Context(), appcontroller.InstallAppOptions{ + IgnoreAppPreflights: tt.ignoreAppPreflights, + ProxySpec: tt.proxySpec, + RegistrySettings: tt.registrySettings, + }) if tt.expectedErr { assert.Error(t, err) diff --git a/api/controllers/kubernetes/install/controller.go b/api/controllers/kubernetes/install/controller.go index 2f81aac023..8e34c34258 100644 --- a/api/controllers/kubernetes/install/controller.go +++ b/api/controllers/kubernetes/install/controller.go @@ -225,6 +225,7 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController, appcontroller.WithPrivateCACertConfigMapName(""), // Private CA ConfigMap functionality not yet implemented for Kubernetes installations appcontroller.WithHelmClient(controller.hcli), appcontroller.WithKubeClient(controller.kcli), + appcontroller.WithMetadataClient(controller.mcli), appcontroller.WithKubernetesEnvSettings(controller.kubernetesEnvSettings), appcontroller.WithPreflightRunner(controller.preflightRunner), ) diff --git a/api/controllers/kubernetes/install/controller_mock.go b/api/controllers/kubernetes/install/controller_mock.go index 9cd176066a..e989750fd3 100644 --- a/api/controllers/kubernetes/install/controller_mock.go +++ b/api/controllers/kubernetes/install/controller_mock.go @@ -109,8 +109,8 @@ func (m *MockController) RunAppPreflights(ctx context.Context, opts appcontrolle } // InstallApp mocks the InstallApp method -func (m *MockController) InstallApp(ctx context.Context, ignoreAppPreflights bool) error { - args := m.Called(ctx, ignoreAppPreflights) +func (m *MockController) InstallApp(ctx context.Context, opts appcontroller.InstallAppOptions) error { + args := m.Called(ctx, opts) return args.Error(0) } diff --git a/api/controllers/kubernetes/upgrade/controller.go b/api/controllers/kubernetes/upgrade/controller.go index 6f7092f302..d74c73cd70 100644 --- a/api/controllers/kubernetes/upgrade/controller.go +++ b/api/controllers/kubernetes/upgrade/controller.go @@ -15,6 +15,7 @@ import ( "github.com/replicatedhq/embedded-cluster/pkg/release" "github.com/sirupsen/logrus" helmcli "helm.sh/helm/v3/pkg/cli" + "k8s.io/client-go/metadata" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -28,6 +29,7 @@ var _ Controller = (*UpgradeController)(nil) type UpgradeController struct { hcli helm.Client kcli client.Client + mcli metadata.Interface preflightRunner preflights.PreflightRunnerInterface kubernetesEnvSettings *helmcli.EnvSettings releaseData *release.ReleaseData @@ -61,6 +63,12 @@ func WithKubeClient(kcli client.Client) UpgradeControllerOption { } } +func WithMetadataClient(mcli metadata.Interface) UpgradeControllerOption { + return func(c *UpgradeController) { + c.mcli = mcli + } +} + func WithPreflightRunner(preflightRunner preflights.PreflightRunnerInterface) UpgradeControllerOption { return func(c *UpgradeController) { c.preflightRunner = preflightRunner @@ -151,6 +159,7 @@ func NewUpgradeController(opts ...UpgradeControllerOption) (*UpgradeController, appcontroller.WithPrivateCACertConfigMapName(""), // Private CA ConfigMap functionality not yet implemented for Kubernetes installations appcontroller.WithHelmClient(controller.hcli), appcontroller.WithKubeClient(controller.kcli), + appcontroller.WithMetadataClient(controller.mcli), appcontroller.WithKubernetesEnvSettings(controller.kubernetesEnvSettings), appcontroller.WithPreflightRunner(controller.preflightRunner), ) diff --git a/api/controllers/linux/install/controller.go b/api/controllers/linux/install/controller.go index f5ec362727..2e6a5dff12 100644 --- a/api/controllers/linux/install/controller.go +++ b/api/controllers/linux/install/controller.go @@ -314,6 +314,7 @@ func NewInstallController(opts ...InstallControllerOption) (*InstallController, appcontroller.WithPrivateCACertConfigMapName(adminconsole.PrivateCASConfigMapName), // Linux installations use the ConfigMap appcontroller.WithHelmClient(controller.hcli), appcontroller.WithKubeClient(controller.kcli), + appcontroller.WithMetadataClient(controller.mcli), appcontroller.WithKubernetesEnvSettings(controller.rc.GetKubernetesEnvSettings()), appcontroller.WithPreflightRunner(controller.preflightRunner), ) diff --git a/api/controllers/linux/install/controller_mock.go b/api/controllers/linux/install/controller_mock.go index 678945baf5..0bbc778284 100644 --- a/api/controllers/linux/install/controller_mock.go +++ b/api/controllers/linux/install/controller_mock.go @@ -142,8 +142,8 @@ func (m *MockController) RunAppPreflights(ctx context.Context, opts appcontrolle } // InstallApp mocks the InstallApp method -func (m *MockController) InstallApp(ctx context.Context, ignoreAppPreflights bool) error { - args := m.Called(ctx, ignoreAppPreflights) +func (m *MockController) InstallApp(ctx context.Context, opts appcontroller.InstallAppOptions) error { + args := m.Called(ctx, opts) return args.Error(0) } diff --git a/api/controllers/linux/install/controller_test.go b/api/controllers/linux/install/controller_test.go index 0c070c4d84..131bd2abcc 100644 --- a/api/controllers/linux/install/controller_test.go +++ b/api/controllers/linux/install/controller_test.go @@ -1177,8 +1177,8 @@ func TestProcessAirgap(t *testing.T) { // Setup expected registry settings expectedRegistrySettings := &types.RegistrySettings{ - Host: "registry.local:5000", - HasLocalRegistry: true, + LocalRegistryHost: "registry.local:5000", + HasLocalRegistry: true, } tt.setupMocks(mockAirgapManager, mockInstallationManager, expectedRegistrySettings, rc, mockStore) diff --git a/api/controllers/linux/upgrade/controller.go b/api/controllers/linux/upgrade/controller.go index 8e170aafb6..4050577ee8 100644 --- a/api/controllers/linux/upgrade/controller.go +++ b/api/controllers/linux/upgrade/controller.go @@ -321,6 +321,7 @@ func NewUpgradeController(opts ...UpgradeControllerOption) (*UpgradeController, appcontroller.WithPrivateCACertConfigMapName(adminconsole.PrivateCASConfigMapName), // Linux upgrades use the ConfigMap appcontroller.WithHelmClient(controller.hcli), appcontroller.WithKubeClient(controller.kcli), + appcontroller.WithMetadataClient(controller.mcli), appcontroller.WithKubernetesEnvSettings(controller.rc.GetKubernetesEnvSettings()), appcontroller.WithPreflightRunner(controller.preflightRunner), ) diff --git a/api/controllers/linux/upgrade/controller_test.go b/api/controllers/linux/upgrade/controller_test.go index 33ef8a2b5a..1af1c41d8a 100644 --- a/api/controllers/linux/upgrade/controller_test.go +++ b/api/controllers/linux/upgrade/controller_test.go @@ -240,8 +240,8 @@ func TestProcessAirgap(t *testing.T) { // Setup expected registry settings expectedRegistrySettings := &types.RegistrySettings{ - Host: "registry.local:5000", - HasLocalRegistry: true, + LocalRegistryHost: "registry.local:5000", + HasLocalRegistry: true, } tt.setupMocks(mockAirgapManager, mockInstallationManager, expectedRegistrySettings, rc, mockStore) diff --git a/api/docs/docs.go b/api/docs/docs.go index c5074c6265..620f1da3ef 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -6,7 +6,7 @@ import "github.com/swaggo/swag/v2" const docTemplate = `{ "schemes": {{ marshal .Schemes }}, - "components": {"schemas":{"github_com_replicatedhq_embedded-cluster_api_types.Health":{"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"github_com_replicatedhq_kotskinds_multitype.BoolOrString":{"type":"object"},"types.APIError":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/types.APIError"},"type":"array","uniqueItems":false},"field":{"type":"string"},"message":{"type":"string"},"statusCode":{"type":"integer"}},"required":["message"],"type":"object"},"types.Airgap":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AppConfig":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigGroup"},"type":"array","uniqueItems":false}},"required":["groups"],"type":"object"},"types.AppConfigValue":{"properties":{"data":{"type":"string"},"dataPlaintext":{"type":"string"},"default":{"type":"string"},"filename":{"type":"string"},"repeatableItem":{"type":"string"},"value":{"type":"string"}},"required":["value"],"type":"object"},"types.AppConfigValues":{"additionalProperties":{"$ref":"#/components/schemas/types.AppConfigValue"},"type":"object"},"types.AppConfigValuesResponse":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.AppInstall":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AppUpgrade":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AuthRequest":{"properties":{"password":{"type":"string"}},"required":["password"],"type":"object"},"types.AuthResponse":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"types.GetListAvailableNetworkInterfacesResponse":{"properties":{"networkInterfaces":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["networkInterfaces"],"type":"object"},"types.Infra":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.InfraComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["components","logs","status"],"type":"object"},"types.InfraComponent":{"properties":{"name":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["name","status"],"type":"object"},"types.InstallAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"hasStrictAppPreflightFailures":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreAppPreflights","hasStrictAppPreflightFailures","status","titles"],"type":"object"},"types.InstallAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"required":["ignoreAppPreflights"],"type":"object"},"types.InstallHostPreflightsStatusResponse":{"properties":{"allowIgnoreHostPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreHostPreflights","titles"],"type":"object"},"types.KURLMigrationPhase":{"description":"Phase is the current phase of the kURL migration process","enum":["Discovery","Preparation","ECInstall","DataTransfer","Completed"],"example":"Discovery","type":"string","x-enum-varnames":["KURLMigrationPhaseDiscovery","KURLMigrationPhasePreparation","KURLMigrationPhaseECInstall","KURLMigrationPhaseDataTransfer","KURLMigrationPhaseCompleted"]},"types.KURLMigrationState":{"description":"State is the current state of the kURL migration","enum":["NotStarted","InProgress","Completed","Failed"],"example":"InProgress","type":"string","x-enum-varnames":["KURLMigrationStateNotStarted","KURLMigrationStateInProgress","KURLMigrationStateCompleted","KURLMigrationStateFailed"]},"types.KURLMigrationStatusResponse":{"description":"Current status and progress of a kURL migration","properties":{"error":{"description":"Error contains the error message if the kURL migration failed","example":"","type":"string"},"message":{"description":"Message is a user-facing message describing the current status","example":"Discovering kURL cluster configuration","type":"string"},"phase":{"$ref":"#/components/schemas/types.KURLMigrationPhase"},"progress":{"description":"Progress is the completion percentage (0-100)","example":25,"type":"integer"},"state":{"$ref":"#/components/schemas/types.KURLMigrationState"}},"required":["message","phase","progress","state"],"type":"object"},"types.KubernetesInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"noProxy":{"type":"string"}},"type":"object"},"types.KubernetesInstallationConfigResponse":{"properties":{"defaults":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"},"resolved":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"},"values":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}},"required":["defaults","resolved","values"],"type":"object"},"types.LinuxInfraSetupRequest":{"properties":{"ignoreHostPreflights":{"type":"boolean"}},"required":["ignoreHostPreflights"],"type":"object"},"types.LinuxInstallationConfig":{"description":"Config contains optional installation configuration that will be merged with defaults","properties":{"dataDirectory":{"type":"string"},"globalCidr":{"type":"string"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"localArtifactMirrorPort":{"type":"integer"},"networkInterface":{"type":"string"},"noProxy":{"type":"string"},"podCidr":{"type":"string"},"serviceCidr":{"type":"string"}},"type":"object"},"types.LinuxInstallationConfigResponse":{"properties":{"defaults":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"resolved":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"values":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}},"required":["defaults","resolved","values"],"type":"object"},"types.PatchAppConfigValuesRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.PostInstallRunHostPreflightsRequest":{"properties":{"isUi":{"type":"boolean"}},"required":["isUi"],"type":"object"},"types.PreflightsOutput":{"properties":{"fail":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"pass":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"warn":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false}},"required":["fail","pass","warn"],"type":"object"},"types.PreflightsRecord":{"properties":{"message":{"type":"string"},"strict":{"type":"boolean"},"title":{"type":"string"}},"required":["message","strict","title"],"type":"object"},"types.StartKURLMigrationRequest":{"description":"Request body for starting a migration from kURL to Embedded Cluster","properties":{"config":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"transferMode":{"$ref":"#/components/schemas/types.TransferMode"}},"required":["transferMode"],"type":"object"},"types.StartKURLMigrationResponse":{"description":"Response returned when a kURL migration is successfully started","properties":{"message":{"description":"Message is a user-facing message about the kURL migration status","example":"kURL migration started successfully","type":"string"},"migrationId":{"description":"MigrationID is the unique identifier for this migration","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}},"required":["message","migrationId"],"type":"object"},"types.State":{"enum":["Pending","Running","Succeeded","Failed"],"example":"Succeeded","type":"string","x-enum-varnames":["StatePending","StateRunning","StateSucceeded","StateFailed"]},"types.Status":{"properties":{"description":{"type":"string"},"lastUpdated":{"type":"string"},"state":{"$ref":"#/components/schemas/types.State"}},"required":["description","lastUpdated","state"],"type":"object"},"types.TemplateAppConfigRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.TransferMode":{"description":"TransferMode specifies whether to copy or move data during kURL migration","enum":["copy","move"],"example":"copy","type":"string","x-enum-varnames":["TransferModeCopy","TransferModeMove"]},"types.UpgradeAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"hasStrictAppPreflightFailures":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreAppPreflights","hasStrictAppPreflightFailures","status","titles"],"type":"object"},"types.UpgradeAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"required":["ignoreAppPreflights"],"type":"object"},"v1beta1.ConfigChildItem":{"properties":{"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"name":{"type":"string"},"recommended":{"type":"boolean"},"title":{"type":"string"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"}},"required":["default","name","recommended","title","value"],"type":"object"},"v1beta1.ConfigGroup":{"properties":{"description":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigItem"},"type":"array","uniqueItems":false},"name":{"type":"string"},"title":{"type":"string"},"when":{"type":"string"}},"required":["description","items","name","title","when"],"type":"object"},"v1beta1.ConfigItem":{"properties":{"affix":{"type":"string"},"countByGroup":{"additionalProperties":{"type":"integer"},"type":"object"},"data":{"type":"string"},"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"error":{"type":"string"},"filename":{"type":"string"},"help_text":{"type":"string"},"hidden":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigChildItem"},"type":"array","uniqueItems":false},"minimumCount":{"type":"integer"},"multi_value":{"items":{"type":"string"},"type":"array","uniqueItems":false},"multiple":{"type":"boolean"},"name":{"type":"string"},"readonly":{"type":"boolean"},"recommended":{"type":"boolean"},"repeatable":{"type":"boolean"},"required":{"type":"boolean"},"templates":{"items":{"$ref":"#/components/schemas/v1beta1.RepeatTemplate"},"type":"array","uniqueItems":false},"title":{"type":"string"},"type":{"type":"string"},"validation":{"$ref":"#/components/schemas/v1beta1.ConfigItemValidation"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"valuesByGroup":{"$ref":"#/components/schemas/v1beta1.ValuesByGroup"},"when":{"type":"string"},"write_once":{"type":"boolean"}},"required":["affix","countByGroup","data","default","error","filename","help_text","hidden","items","minimumCount","multi_value","multiple","name","readonly","recommended","repeatable","required","templates","title","type","validation","value","valuesByGroup","when","write_once"],"type":"object"},"v1beta1.ConfigItemValidation":{"properties":{"regex":{"$ref":"#/components/schemas/v1beta1.RegexValidator"}},"required":["regex"],"type":"object"},"v1beta1.GroupValues":{"additionalProperties":{"type":"string"},"type":"object"},"v1beta1.RegexValidator":{"properties":{"message":{"type":"string"},"pattern":{"type":"string"}},"required":["message","pattern"],"type":"object"},"v1beta1.RepeatTemplate":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"yamlPath":{"type":"string"}},"required":["apiVersion","kind","name","namespace","yamlPath"],"type":"object"},"v1beta1.ValuesByGroup":{"additionalProperties":{"$ref":"#/components/schemas/v1beta1.GroupValues"},"type":"object"}},"securitySchemes":{"bearerauth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"}}}, + "components": {"schemas":{"github_com_replicatedhq_embedded-cluster_api_types.Health":{"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"github_com_replicatedhq_kotskinds_multitype.BoolOrString":{"type":"object"},"types.APIError":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/types.APIError"},"type":"array","uniqueItems":false},"field":{"type":"string"},"message":{"type":"string"},"statusCode":{"type":"integer"}},"required":["message"],"type":"object"},"types.Airgap":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AppComponent":{"properties":{"name":{"description":"Chart name","type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["name","status"],"type":"object"},"types.AppConfig":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigGroup"},"type":"array","uniqueItems":false}},"required":["groups"],"type":"object"},"types.AppConfigValue":{"properties":{"data":{"type":"string"},"dataPlaintext":{"type":"string"},"default":{"type":"string"},"filename":{"type":"string"},"repeatableItem":{"type":"string"},"value":{"type":"string"}},"required":["value"],"type":"object"},"types.AppConfigValues":{"additionalProperties":{"$ref":"#/components/schemas/types.AppConfigValue"},"type":"object"},"types.AppConfigValuesResponse":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.AppInstall":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.AppComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["components","logs","status"],"type":"object"},"types.AppUpgrade":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AuthRequest":{"properties":{"password":{"type":"string"}},"required":["password"],"type":"object"},"types.AuthResponse":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"types.GetListAvailableNetworkInterfacesResponse":{"properties":{"networkInterfaces":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["networkInterfaces"],"type":"object"},"types.Infra":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.InfraComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["components","logs","status"],"type":"object"},"types.InfraComponent":{"properties":{"name":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["name","status"],"type":"object"},"types.InstallAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"hasStrictAppPreflightFailures":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreAppPreflights","hasStrictAppPreflightFailures","status","titles"],"type":"object"},"types.InstallAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"required":["ignoreAppPreflights"],"type":"object"},"types.InstallHostPreflightsStatusResponse":{"properties":{"allowIgnoreHostPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreHostPreflights","titles"],"type":"object"},"types.KURLMigrationPhase":{"description":"Phase is the current phase of the kURL migration process","enum":["Discovery","Preparation","ECInstall","DataTransfer","Completed"],"example":"Discovery","type":"string","x-enum-varnames":["KURLMigrationPhaseDiscovery","KURLMigrationPhasePreparation","KURLMigrationPhaseECInstall","KURLMigrationPhaseDataTransfer","KURLMigrationPhaseCompleted"]},"types.KURLMigrationState":{"description":"State is the current state of the kURL migration","enum":["NotStarted","InProgress","Completed","Failed"],"example":"InProgress","type":"string","x-enum-varnames":["KURLMigrationStateNotStarted","KURLMigrationStateInProgress","KURLMigrationStateCompleted","KURLMigrationStateFailed"]},"types.KURLMigrationStatusResponse":{"description":"Current status and progress of a kURL migration","properties":{"error":{"description":"Error contains the error message if the kURL migration failed","example":"","type":"string"},"message":{"description":"Message is a user-facing message describing the current status","example":"Discovering kURL cluster configuration","type":"string"},"phase":{"$ref":"#/components/schemas/types.KURLMigrationPhase"},"progress":{"description":"Progress is the completion percentage (0-100)","example":25,"type":"integer"},"state":{"$ref":"#/components/schemas/types.KURLMigrationState"}},"required":["message","phase","progress","state"],"type":"object"},"types.KubernetesInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"noProxy":{"type":"string"}},"type":"object"},"types.KubernetesInstallationConfigResponse":{"properties":{"defaults":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"},"resolved":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"},"values":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}},"required":["defaults","resolved","values"],"type":"object"},"types.LinuxInfraSetupRequest":{"properties":{"ignoreHostPreflights":{"type":"boolean"}},"required":["ignoreHostPreflights"],"type":"object"},"types.LinuxInstallationConfig":{"description":"Config contains optional installation configuration that will be merged with defaults","properties":{"dataDirectory":{"type":"string"},"globalCidr":{"type":"string"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"localArtifactMirrorPort":{"type":"integer"},"networkInterface":{"type":"string"},"noProxy":{"type":"string"},"podCidr":{"type":"string"},"serviceCidr":{"type":"string"}},"type":"object"},"types.LinuxInstallationConfigResponse":{"properties":{"defaults":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"resolved":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"values":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}},"required":["defaults","resolved","values"],"type":"object"},"types.PatchAppConfigValuesRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.PostInstallRunHostPreflightsRequest":{"properties":{"isUi":{"type":"boolean"}},"required":["isUi"],"type":"object"},"types.PreflightsOutput":{"properties":{"fail":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"pass":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"warn":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false}},"required":["fail","pass","warn"],"type":"object"},"types.PreflightsRecord":{"properties":{"message":{"type":"string"},"strict":{"type":"boolean"},"title":{"type":"string"}},"required":["message","strict","title"],"type":"object"},"types.StartKURLMigrationRequest":{"description":"Request body for starting a migration from kURL to Embedded Cluster","properties":{"config":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"transferMode":{"$ref":"#/components/schemas/types.TransferMode"}},"required":["transferMode"],"type":"object"},"types.StartKURLMigrationResponse":{"description":"Response returned when a kURL migration is successfully started","properties":{"message":{"description":"Message is a user-facing message about the kURL migration status","example":"kURL migration started successfully","type":"string"},"migrationId":{"description":"MigrationID is the unique identifier for this migration","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}},"required":["message","migrationId"],"type":"object"},"types.State":{"enum":["Pending","Running","Succeeded","Failed"],"example":"Succeeded","type":"string","x-enum-varnames":["StatePending","StateRunning","StateSucceeded","StateFailed"]},"types.Status":{"description":"Uses existing Status type","properties":{"description":{"type":"string"},"lastUpdated":{"type":"string"},"state":{"$ref":"#/components/schemas/types.State"}},"required":["description","lastUpdated","state"],"type":"object"},"types.TemplateAppConfigRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.TransferMode":{"description":"TransferMode specifies whether to copy or move data during kURL migration","enum":["copy","move"],"example":"copy","type":"string","x-enum-varnames":["TransferModeCopy","TransferModeMove"]},"types.UpgradeAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"hasStrictAppPreflightFailures":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreAppPreflights","hasStrictAppPreflightFailures","status","titles"],"type":"object"},"types.UpgradeAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"required":["ignoreAppPreflights"],"type":"object"},"v1beta1.ConfigChildItem":{"properties":{"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"name":{"type":"string"},"recommended":{"type":"boolean"},"title":{"type":"string"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"}},"required":["default","name","recommended","title","value"],"type":"object"},"v1beta1.ConfigGroup":{"properties":{"description":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigItem"},"type":"array","uniqueItems":false},"name":{"type":"string"},"title":{"type":"string"},"when":{"type":"string"}},"required":["description","items","name","title","when"],"type":"object"},"v1beta1.ConfigItem":{"properties":{"affix":{"type":"string"},"countByGroup":{"additionalProperties":{"type":"integer"},"type":"object"},"data":{"type":"string"},"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"error":{"type":"string"},"filename":{"type":"string"},"help_text":{"type":"string"},"hidden":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigChildItem"},"type":"array","uniqueItems":false},"minimumCount":{"type":"integer"},"multi_value":{"items":{"type":"string"},"type":"array","uniqueItems":false},"multiple":{"type":"boolean"},"name":{"type":"string"},"readonly":{"type":"boolean"},"recommended":{"type":"boolean"},"repeatable":{"type":"boolean"},"required":{"type":"boolean"},"templates":{"items":{"$ref":"#/components/schemas/v1beta1.RepeatTemplate"},"type":"array","uniqueItems":false},"title":{"type":"string"},"type":{"type":"string"},"validation":{"$ref":"#/components/schemas/v1beta1.ConfigItemValidation"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"valuesByGroup":{"$ref":"#/components/schemas/v1beta1.ValuesByGroup"},"when":{"type":"string"},"write_once":{"type":"boolean"}},"required":["affix","countByGroup","data","default","error","filename","help_text","hidden","items","minimumCount","multi_value","multiple","name","readonly","recommended","repeatable","required","templates","title","type","validation","value","valuesByGroup","when","write_once"],"type":"object"},"v1beta1.ConfigItemValidation":{"properties":{"regex":{"$ref":"#/components/schemas/v1beta1.RegexValidator"}},"required":["regex"],"type":"object"},"v1beta1.GroupValues":{"additionalProperties":{"type":"string"},"type":"object"},"v1beta1.RegexValidator":{"properties":{"message":{"type":"string"},"pattern":{"type":"string"}},"required":["message","pattern"],"type":"object"},"v1beta1.RepeatTemplate":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"yamlPath":{"type":"string"}},"required":["apiVersion","kind","name","namespace","yamlPath"],"type":"object"},"v1beta1.ValuesByGroup":{"additionalProperties":{"$ref":"#/components/schemas/v1beta1.GroupValues"},"type":"object"}},"securitySchemes":{"bearerauth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"}}}, "info": {"contact":{"email":"support@replicated.com","name":"API Support","url":"https://github.com/replicatedhq/embedded-cluster/issues"},"description":"{{escape .Description}}","license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"termsOfService":"http://swagger.io/terms/","title":"{{.Title}}","version":"{{.Version}}"}, "externalDocs": {"description":"OpenAPI","url":"https://swagger.io/resources/open-api/"}, "paths": {"/auth/login":{"post":{"description":"Authenticate a user","operationId":"postAuthLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AuthRequest"}}},"description":"Auth Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AuthResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Unauthorized"}},"summary":"Authenticate a user","tags":["auth"]}},"/console/available-network-interfaces":{"get":{"description":"List available network interfaces","operationId":"getConsoleListAvailableNetworkInterfaces","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.GetListAvailableNetworkInterfacesResponse"}}},"description":"OK"}},"summary":"List available network interfaces","tags":["console"]}},"/health":{"get":{"description":"get the health of the API","operationId":"getHealth","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/github_com_replicatedhq_embedded-cluster_api_types.Health"}}},"description":"OK"}},"summary":"Get the health of the API","tags":["health"]}},"/kubernetes/install/app-preflights/run":{"post":{"description":"Run install app preflight checks using current app configuration","operationId":"postKubernetesInstallRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run install app preflight checks","tags":["kubernetes-install"]}},"/kubernetes/install/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for install","operationId":"getKubernetesInstallAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for install","tags":["kubernetes-install"]}},"/kubernetes/install/app/config/template":{"post":{"description":"Template the app config with provided values and return the templated config","operationId":"postKubernetesInstallTemplateAppConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template the app config with provided values","tags":["kubernetes-install"]}},"/kubernetes/install/app/config/values":{"get":{"description":"Get the current app config values","operationId":"getKubernetesInstallAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values","tags":["kubernetes-install"]},"patch":{"description":"Set the app config values with partial updates","operationId":"patchKubernetesInstallAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values","tags":["kubernetes-install"]}},"/kubernetes/install/app/install":{"post":{"description":"Install the app using current configuration","operationId":"postKubernetesInstallApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppRequest"}}},"description":"Install App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Install the app","tags":["kubernetes-install"]}},"/kubernetes/install/app/status":{"get":{"description":"Get the current status of app installation","operationId":"getKubernetesInstallAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app install status","tags":["kubernetes-install"]}},"/kubernetes/install/infra/setup":{"post":{"description":"Setup infra components","operationId":"postKubernetesInstallSetupInfra","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Setup infra components","tags":["kubernetes-install"]}},"/kubernetes/install/infra/status":{"get":{"description":"Get the current status of the infra","operationId":"getKubernetesInstallInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra","tags":["kubernetes-install"]}},"/kubernetes/install/installation/config":{"get":{"description":"get the Kubernetes installation config","operationId":"getKubernetesInstallInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KubernetesInstallationConfigResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the Kubernetes installation config","tags":["kubernetes-install"]}},"/kubernetes/install/installation/configure":{"post":{"description":"configure the Kubernetes installation for install","operationId":"postKubernetesInstallConfigureInstallation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}}},"description":"Installation config","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Configure the Kubernetes installation for install","tags":["kubernetes-install"]}},"/kubernetes/install/installation/status":{"get":{"description":"Get the current status of the installation configuration for install","operationId":"getKubernetesInstallInstallationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get installation configuration status for install","tags":["kubernetes-install"]}},"/kubernetes/upgrade/app-preflights/run":{"post":{"description":"Run upgrade app preflight checks using current app configuration","operationId":"postKubernetesUpgradeRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run upgrade app preflight checks","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for upgrade","operationId":"getKubernetesUpgradeAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for upgrade","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app/config/template":{"post":{"description":"Template the app configuration with values for upgrade","operationId":"postKubernetesUpgradeAppConfigTemplate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template app config for upgrade","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app/config/values":{"get":{"description":"Get the current app config values for upgrade","operationId":"getKubernetesUpgradeAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values for upgrade","tags":["kubernetes-upgrade"]},"patch":{"description":"Set the app config values with partial updates for upgrade","operationId":"patchKubernetesUpgradeAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values for upgrade","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app/status":{"get":{"description":"Get the current status of app upgrade","operationId":"getKubernetesUpgradeAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppUpgrade"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app upgrade status","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app/upgrade":{"post":{"description":"Upgrade the app using current configuration","operationId":"postKubernetesUpgradeApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppRequest"}}},"description":"Upgrade App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppUpgrade"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Upgrade the app","tags":["kubernetes-upgrade"]}},"/linux/install/airgap/process":{"post":{"description":"Process the airgap bundle for install","operationId":"postLinuxInstallProcessAirgap","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Airgap"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Process the airgap bundle","tags":["linux-install"]}},"/linux/install/airgap/status":{"get":{"description":"Get the current status of the airgap processing for install","operationId":"getLinuxInstallAirgapStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Airgap"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the airgap processing","tags":["linux-install"]}},"/linux/install/app-preflights/run":{"post":{"description":"Run install app preflight checks using current app configuration","operationId":"postLinuxInstallRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run install app preflight checks","tags":["linux-install"]}},"/linux/install/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for install","operationId":"getLinuxInstallAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for install","tags":["linux-install"]}},"/linux/install/app/config/template":{"post":{"description":"Template the app config with provided values and return the templated config","operationId":"postLinuxInstallTemplateAppConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template the app config with provided values","tags":["linux-install"]}},"/linux/install/app/config/values":{"get":{"description":"Get the current app config values","operationId":"getLinuxInstallAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values","tags":["linux-install"]},"patch":{"description":"Set the app config values with partial updates","operationId":"patchLinuxInstallAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values","tags":["linux-install"]}},"/linux/install/app/install":{"post":{"description":"Install the app using current configuration","operationId":"postLinuxInstallApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppRequest"}}},"description":"Install App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Install the app","tags":["linux-install"]}},"/linux/install/app/status":{"get":{"description":"Get the current status of app installation","operationId":"getLinuxInstallAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app install status","tags":["linux-install"]}},"/linux/install/host-preflights/run":{"post":{"description":"Run install host preflight checks using installation config and client-provided data","operationId":"postLinuxInstallRunHostPreflights","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PostInstallRunHostPreflightsRequest"}}},"description":"Post Install Run Host Preflights Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallHostPreflightsStatusResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Run install host preflight checks","tags":["linux-install"]}},"/linux/install/host-preflights/status":{"get":{"description":"Get the current status and results of host preflight checks for install","operationId":"getLinuxInstallHostPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallHostPreflightsStatusResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get host preflight status for install","tags":["linux-install"]}},"/linux/install/infra/setup":{"post":{"description":"Setup infra components","operationId":"postLinuxInstallSetupInfra","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInfraSetupRequest"}}},"description":"Infra Setup Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Setup infra components","tags":["linux-install"]}},"/linux/install/infra/status":{"get":{"description":"Get the current status of the infra","operationId":"getLinuxInstallInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra","tags":["linux-install"]}},"/linux/install/installation/config":{"get":{"description":"get the installation config","operationId":"getLinuxInstallInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfigResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the installation config","tags":["linux-install"]}},"/linux/install/installation/configure":{"post":{"description":"configure the installation for install","operationId":"postLinuxInstallConfigureInstallation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}}},"description":"Installation config","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Configure the installation for install","tags":["linux-install"]}},"/linux/install/installation/status":{"get":{"description":"Get the current status of the installation configuration for install","operationId":"getLinuxInstallInstallationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get installation configuration status for install","tags":["linux-install"]}},"/linux/kurl-migration/config":{"get":{"description":"Get the installation config extracted from kURL merged with EC defaults","operationId":"getKURLMigrationInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfigResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the installation config for kURL migration","tags":["kurl-migration"]}},"/linux/kurl-migration/start":{"post":{"description":"Start a migration from kURL to Embedded Cluster with the provided configuration","operationId":"postStartMigration","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.StartKURLMigrationRequest"}}},"description":"Start kURL Migration Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.StartKURLMigrationResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Conflict"}},"security":[{"bearerauth":[]}],"summary":"Start a migration from kURL to Embedded Cluster","tags":["kurl-migration"]}},"/linux/kurl-migration/status":{"get":{"description":"Get the current status and progress of the kURL migration","operationId":"getKURLMigrationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KURLMigrationStatusResponse"}}},"description":"OK"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Not Found"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the kURL migration","tags":["kurl-migration"]}},"/linux/upgrade/airgap/process":{"post":{"description":"Process the airgap bundle for upgrade","operationId":"postLinuxUpgradeProcessAirgap","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Airgap"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Process the airgap bundle","tags":["linux-upgrade"]}},"/linux/upgrade/airgap/status":{"get":{"description":"Get the current status of the airgap processing for upgrade","operationId":"getLinuxUpgradeAirgapStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Airgap"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the airgap processing","tags":["linux-upgrade"]}},"/linux/upgrade/app-preflights/run":{"post":{"description":"Run upgrade app preflight checks using current app configuration","operationId":"postLinuxUpgradeRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run upgrade app preflight checks","tags":["linux-upgrade"]}},"/linux/upgrade/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for upgrade","operationId":"getLinuxUpgradeAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for upgrade","tags":["linux-upgrade"]}},"/linux/upgrade/app/config/template":{"post":{"description":"Template the app configuration with values for upgrade","operationId":"postLinuxUpgradeAppConfigTemplate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template app config for upgrade","tags":["linux-upgrade"]}},"/linux/upgrade/app/config/values":{"get":{"description":"Get the current app config values for upgrade","operationId":"getLinuxUpgradeAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values for upgrade","tags":["linux-upgrade"]},"patch":{"description":"Set the app config values with partial updates for upgrade","operationId":"patchLinuxUpgradeAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values for upgrade","tags":["linux-upgrade"]}},"/linux/upgrade/app/status":{"get":{"description":"Get the current status of app upgrade","operationId":"getLinuxUpgradeAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppUpgrade"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app upgrade status","tags":["linux-upgrade"]}},"/linux/upgrade/app/upgrade":{"post":{"description":"Upgrade the app using current configuration","operationId":"postLinuxUpgradeApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppRequest"}}},"description":"Upgrade App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppUpgrade"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Upgrade the app","tags":["linux-upgrade"]}},"/linux/upgrade/infra/status":{"get":{"description":"Get the current status of the infrastructure upgrade","operationId":"getLinuxUpgradeInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Internal Server Error"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra upgrade","tags":["linux-upgrade"]}},"/linux/upgrade/infra/upgrade":{"post":{"description":"Upgrade the infrastructure (k0s, addons, extensions)","operationId":"postLinuxUpgradeInfra","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Unauthorized"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Conflict"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Internal Server Error"}},"security":[{"bearerauth":[]}],"summary":"Upgrade the infrastructure","tags":["linux-upgrade"]}}}, diff --git a/api/docs/swagger.json b/api/docs/swagger.json index addf659448..2e0d74438c 100644 --- a/api/docs/swagger.json +++ b/api/docs/swagger.json @@ -1,5 +1,5 @@ { - "components": {"schemas":{"github_com_replicatedhq_embedded-cluster_api_types.Health":{"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"github_com_replicatedhq_kotskinds_multitype.BoolOrString":{"type":"object"},"types.APIError":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/types.APIError"},"type":"array","uniqueItems":false},"field":{"type":"string"},"message":{"type":"string"},"statusCode":{"type":"integer"}},"required":["message"],"type":"object"},"types.Airgap":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AppConfig":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigGroup"},"type":"array","uniqueItems":false}},"required":["groups"],"type":"object"},"types.AppConfigValue":{"properties":{"data":{"type":"string"},"dataPlaintext":{"type":"string"},"default":{"type":"string"},"filename":{"type":"string"},"repeatableItem":{"type":"string"},"value":{"type":"string"}},"required":["value"],"type":"object"},"types.AppConfigValues":{"additionalProperties":{"$ref":"#/components/schemas/types.AppConfigValue"},"type":"object"},"types.AppConfigValuesResponse":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.AppInstall":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AppUpgrade":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AuthRequest":{"properties":{"password":{"type":"string"}},"required":["password"],"type":"object"},"types.AuthResponse":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"types.GetListAvailableNetworkInterfacesResponse":{"properties":{"networkInterfaces":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["networkInterfaces"],"type":"object"},"types.Infra":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.InfraComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["components","logs","status"],"type":"object"},"types.InfraComponent":{"properties":{"name":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["name","status"],"type":"object"},"types.InstallAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"hasStrictAppPreflightFailures":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreAppPreflights","hasStrictAppPreflightFailures","status","titles"],"type":"object"},"types.InstallAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"required":["ignoreAppPreflights"],"type":"object"},"types.InstallHostPreflightsStatusResponse":{"properties":{"allowIgnoreHostPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreHostPreflights","titles"],"type":"object"},"types.KURLMigrationPhase":{"description":"Phase is the current phase of the kURL migration process","enum":["Discovery","Preparation","ECInstall","DataTransfer","Completed"],"example":"Discovery","type":"string","x-enum-varnames":["KURLMigrationPhaseDiscovery","KURLMigrationPhasePreparation","KURLMigrationPhaseECInstall","KURLMigrationPhaseDataTransfer","KURLMigrationPhaseCompleted"]},"types.KURLMigrationState":{"description":"State is the current state of the kURL migration","enum":["NotStarted","InProgress","Completed","Failed"],"example":"InProgress","type":"string","x-enum-varnames":["KURLMigrationStateNotStarted","KURLMigrationStateInProgress","KURLMigrationStateCompleted","KURLMigrationStateFailed"]},"types.KURLMigrationStatusResponse":{"description":"Current status and progress of a kURL migration","properties":{"error":{"description":"Error contains the error message if the kURL migration failed","example":"","type":"string"},"message":{"description":"Message is a user-facing message describing the current status","example":"Discovering kURL cluster configuration","type":"string"},"phase":{"$ref":"#/components/schemas/types.KURLMigrationPhase"},"progress":{"description":"Progress is the completion percentage (0-100)","example":25,"type":"integer"},"state":{"$ref":"#/components/schemas/types.KURLMigrationState"}},"required":["message","phase","progress","state"],"type":"object"},"types.KubernetesInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"noProxy":{"type":"string"}},"type":"object"},"types.KubernetesInstallationConfigResponse":{"properties":{"defaults":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"},"resolved":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"},"values":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}},"required":["defaults","resolved","values"],"type":"object"},"types.LinuxInfraSetupRequest":{"properties":{"ignoreHostPreflights":{"type":"boolean"}},"required":["ignoreHostPreflights"],"type":"object"},"types.LinuxInstallationConfig":{"description":"Config contains optional installation configuration that will be merged with defaults","properties":{"dataDirectory":{"type":"string"},"globalCidr":{"type":"string"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"localArtifactMirrorPort":{"type":"integer"},"networkInterface":{"type":"string"},"noProxy":{"type":"string"},"podCidr":{"type":"string"},"serviceCidr":{"type":"string"}},"type":"object"},"types.LinuxInstallationConfigResponse":{"properties":{"defaults":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"resolved":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"values":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}},"required":["defaults","resolved","values"],"type":"object"},"types.PatchAppConfigValuesRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.PostInstallRunHostPreflightsRequest":{"properties":{"isUi":{"type":"boolean"}},"required":["isUi"],"type":"object"},"types.PreflightsOutput":{"properties":{"fail":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"pass":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"warn":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false}},"required":["fail","pass","warn"],"type":"object"},"types.PreflightsRecord":{"properties":{"message":{"type":"string"},"strict":{"type":"boolean"},"title":{"type":"string"}},"required":["message","strict","title"],"type":"object"},"types.StartKURLMigrationRequest":{"description":"Request body for starting a migration from kURL to Embedded Cluster","properties":{"config":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"transferMode":{"$ref":"#/components/schemas/types.TransferMode"}},"required":["transferMode"],"type":"object"},"types.StartKURLMigrationResponse":{"description":"Response returned when a kURL migration is successfully started","properties":{"message":{"description":"Message is a user-facing message about the kURL migration status","example":"kURL migration started successfully","type":"string"},"migrationId":{"description":"MigrationID is the unique identifier for this migration","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}},"required":["message","migrationId"],"type":"object"},"types.State":{"enum":["Pending","Running","Succeeded","Failed"],"example":"Succeeded","type":"string","x-enum-varnames":["StatePending","StateRunning","StateSucceeded","StateFailed"]},"types.Status":{"properties":{"description":{"type":"string"},"lastUpdated":{"type":"string"},"state":{"$ref":"#/components/schemas/types.State"}},"required":["description","lastUpdated","state"],"type":"object"},"types.TemplateAppConfigRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.TransferMode":{"description":"TransferMode specifies whether to copy or move data during kURL migration","enum":["copy","move"],"example":"copy","type":"string","x-enum-varnames":["TransferModeCopy","TransferModeMove"]},"types.UpgradeAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"hasStrictAppPreflightFailures":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreAppPreflights","hasStrictAppPreflightFailures","status","titles"],"type":"object"},"types.UpgradeAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"required":["ignoreAppPreflights"],"type":"object"},"v1beta1.ConfigChildItem":{"properties":{"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"name":{"type":"string"},"recommended":{"type":"boolean"},"title":{"type":"string"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"}},"required":["default","name","recommended","title","value"],"type":"object"},"v1beta1.ConfigGroup":{"properties":{"description":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigItem"},"type":"array","uniqueItems":false},"name":{"type":"string"},"title":{"type":"string"},"when":{"type":"string"}},"required":["description","items","name","title","when"],"type":"object"},"v1beta1.ConfigItem":{"properties":{"affix":{"type":"string"},"countByGroup":{"additionalProperties":{"type":"integer"},"type":"object"},"data":{"type":"string"},"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"error":{"type":"string"},"filename":{"type":"string"},"help_text":{"type":"string"},"hidden":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigChildItem"},"type":"array","uniqueItems":false},"minimumCount":{"type":"integer"},"multi_value":{"items":{"type":"string"},"type":"array","uniqueItems":false},"multiple":{"type":"boolean"},"name":{"type":"string"},"readonly":{"type":"boolean"},"recommended":{"type":"boolean"},"repeatable":{"type":"boolean"},"required":{"type":"boolean"},"templates":{"items":{"$ref":"#/components/schemas/v1beta1.RepeatTemplate"},"type":"array","uniqueItems":false},"title":{"type":"string"},"type":{"type":"string"},"validation":{"$ref":"#/components/schemas/v1beta1.ConfigItemValidation"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"valuesByGroup":{"$ref":"#/components/schemas/v1beta1.ValuesByGroup"},"when":{"type":"string"},"write_once":{"type":"boolean"}},"required":["affix","countByGroup","data","default","error","filename","help_text","hidden","items","minimumCount","multi_value","multiple","name","readonly","recommended","repeatable","required","templates","title","type","validation","value","valuesByGroup","when","write_once"],"type":"object"},"v1beta1.ConfigItemValidation":{"properties":{"regex":{"$ref":"#/components/schemas/v1beta1.RegexValidator"}},"required":["regex"],"type":"object"},"v1beta1.GroupValues":{"additionalProperties":{"type":"string"},"type":"object"},"v1beta1.RegexValidator":{"properties":{"message":{"type":"string"},"pattern":{"type":"string"}},"required":["message","pattern"],"type":"object"},"v1beta1.RepeatTemplate":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"yamlPath":{"type":"string"}},"required":["apiVersion","kind","name","namespace","yamlPath"],"type":"object"},"v1beta1.ValuesByGroup":{"additionalProperties":{"$ref":"#/components/schemas/v1beta1.GroupValues"},"type":"object"}},"securitySchemes":{"bearerauth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"}}}, + "components": {"schemas":{"github_com_replicatedhq_embedded-cluster_api_types.Health":{"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"github_com_replicatedhq_kotskinds_multitype.BoolOrString":{"type":"object"},"types.APIError":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/types.APIError"},"type":"array","uniqueItems":false},"field":{"type":"string"},"message":{"type":"string"},"statusCode":{"type":"integer"}},"required":["message"],"type":"object"},"types.Airgap":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AppComponent":{"properties":{"name":{"description":"Chart name","type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["name","status"],"type":"object"},"types.AppConfig":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigGroup"},"type":"array","uniqueItems":false}},"required":["groups"],"type":"object"},"types.AppConfigValue":{"properties":{"data":{"type":"string"},"dataPlaintext":{"type":"string"},"default":{"type":"string"},"filename":{"type":"string"},"repeatableItem":{"type":"string"},"value":{"type":"string"}},"required":["value"],"type":"object"},"types.AppConfigValues":{"additionalProperties":{"$ref":"#/components/schemas/types.AppConfigValue"},"type":"object"},"types.AppConfigValuesResponse":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.AppInstall":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.AppComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["components","logs","status"],"type":"object"},"types.AppUpgrade":{"properties":{"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["logs","status"],"type":"object"},"types.AuthRequest":{"properties":{"password":{"type":"string"}},"required":["password"],"type":"object"},"types.AuthResponse":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"types.GetListAvailableNetworkInterfacesResponse":{"properties":{"networkInterfaces":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["networkInterfaces"],"type":"object"},"types.Infra":{"properties":{"components":{"items":{"$ref":"#/components/schemas/types.InfraComponent"},"type":"array","uniqueItems":false},"logs":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["components","logs","status"],"type":"object"},"types.InfraComponent":{"properties":{"name":{"type":"string"},"status":{"$ref":"#/components/schemas/types.Status"}},"required":["name","status"],"type":"object"},"types.InstallAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"hasStrictAppPreflightFailures":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreAppPreflights","hasStrictAppPreflightFailures","status","titles"],"type":"object"},"types.InstallAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"required":["ignoreAppPreflights"],"type":"object"},"types.InstallHostPreflightsStatusResponse":{"properties":{"allowIgnoreHostPreflights":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreHostPreflights","titles"],"type":"object"},"types.KURLMigrationPhase":{"description":"Phase is the current phase of the kURL migration process","enum":["Discovery","Preparation","ECInstall","DataTransfer","Completed"],"example":"Discovery","type":"string","x-enum-varnames":["KURLMigrationPhaseDiscovery","KURLMigrationPhasePreparation","KURLMigrationPhaseECInstall","KURLMigrationPhaseDataTransfer","KURLMigrationPhaseCompleted"]},"types.KURLMigrationState":{"description":"State is the current state of the kURL migration","enum":["NotStarted","InProgress","Completed","Failed"],"example":"InProgress","type":"string","x-enum-varnames":["KURLMigrationStateNotStarted","KURLMigrationStateInProgress","KURLMigrationStateCompleted","KURLMigrationStateFailed"]},"types.KURLMigrationStatusResponse":{"description":"Current status and progress of a kURL migration","properties":{"error":{"description":"Error contains the error message if the kURL migration failed","example":"","type":"string"},"message":{"description":"Message is a user-facing message describing the current status","example":"Discovering kURL cluster configuration","type":"string"},"phase":{"$ref":"#/components/schemas/types.KURLMigrationPhase"},"progress":{"description":"Progress is the completion percentage (0-100)","example":25,"type":"integer"},"state":{"$ref":"#/components/schemas/types.KURLMigrationState"}},"required":["message","phase","progress","state"],"type":"object"},"types.KubernetesInstallationConfig":{"properties":{"adminConsolePort":{"type":"integer"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"noProxy":{"type":"string"}},"type":"object"},"types.KubernetesInstallationConfigResponse":{"properties":{"defaults":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"},"resolved":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"},"values":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}},"required":["defaults","resolved","values"],"type":"object"},"types.LinuxInfraSetupRequest":{"properties":{"ignoreHostPreflights":{"type":"boolean"}},"required":["ignoreHostPreflights"],"type":"object"},"types.LinuxInstallationConfig":{"description":"Config contains optional installation configuration that will be merged with defaults","properties":{"dataDirectory":{"type":"string"},"globalCidr":{"type":"string"},"httpProxy":{"type":"string"},"httpsProxy":{"type":"string"},"localArtifactMirrorPort":{"type":"integer"},"networkInterface":{"type":"string"},"noProxy":{"type":"string"},"podCidr":{"type":"string"},"serviceCidr":{"type":"string"}},"type":"object"},"types.LinuxInstallationConfigResponse":{"properties":{"defaults":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"resolved":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"values":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}},"required":["defaults","resolved","values"],"type":"object"},"types.PatchAppConfigValuesRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.PostInstallRunHostPreflightsRequest":{"properties":{"isUi":{"type":"boolean"}},"required":["isUi"],"type":"object"},"types.PreflightsOutput":{"properties":{"fail":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"pass":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false},"warn":{"items":{"$ref":"#/components/schemas/types.PreflightsRecord"},"type":"array","uniqueItems":false}},"required":["fail","pass","warn"],"type":"object"},"types.PreflightsRecord":{"properties":{"message":{"type":"string"},"strict":{"type":"boolean"},"title":{"type":"string"}},"required":["message","strict","title"],"type":"object"},"types.StartKURLMigrationRequest":{"description":"Request body for starting a migration from kURL to Embedded Cluster","properties":{"config":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"},"transferMode":{"$ref":"#/components/schemas/types.TransferMode"}},"required":["transferMode"],"type":"object"},"types.StartKURLMigrationResponse":{"description":"Response returned when a kURL migration is successfully started","properties":{"message":{"description":"Message is a user-facing message about the kURL migration status","example":"kURL migration started successfully","type":"string"},"migrationId":{"description":"MigrationID is the unique identifier for this migration","example":"550e8400-e29b-41d4-a716-446655440000","type":"string"}},"required":["message","migrationId"],"type":"object"},"types.State":{"enum":["Pending","Running","Succeeded","Failed"],"example":"Succeeded","type":"string","x-enum-varnames":["StatePending","StateRunning","StateSucceeded","StateFailed"]},"types.Status":{"description":"Uses existing Status type","properties":{"description":{"type":"string"},"lastUpdated":{"type":"string"},"state":{"$ref":"#/components/schemas/types.State"}},"required":["description","lastUpdated","state"],"type":"object"},"types.TemplateAppConfigRequest":{"properties":{"values":{"$ref":"#/components/schemas/types.AppConfigValues"}},"required":["values"],"type":"object"},"types.TransferMode":{"description":"TransferMode specifies whether to copy or move data during kURL migration","enum":["copy","move"],"example":"copy","type":"string","x-enum-varnames":["TransferModeCopy","TransferModeMove"]},"types.UpgradeAppPreflightsStatusResponse":{"properties":{"allowIgnoreAppPreflights":{"type":"boolean"},"hasStrictAppPreflightFailures":{"type":"boolean"},"output":{"$ref":"#/components/schemas/types.PreflightsOutput"},"status":{"$ref":"#/components/schemas/types.Status"},"titles":{"items":{"type":"string"},"type":"array","uniqueItems":false}},"required":["allowIgnoreAppPreflights","hasStrictAppPreflightFailures","status","titles"],"type":"object"},"types.UpgradeAppRequest":{"properties":{"ignoreAppPreflights":{"type":"boolean"}},"required":["ignoreAppPreflights"],"type":"object"},"v1beta1.ConfigChildItem":{"properties":{"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"name":{"type":"string"},"recommended":{"type":"boolean"},"title":{"type":"string"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"}},"required":["default","name","recommended","title","value"],"type":"object"},"v1beta1.ConfigGroup":{"properties":{"description":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigItem"},"type":"array","uniqueItems":false},"name":{"type":"string"},"title":{"type":"string"},"when":{"type":"string"}},"required":["description","items","name","title","when"],"type":"object"},"v1beta1.ConfigItem":{"properties":{"affix":{"type":"string"},"countByGroup":{"additionalProperties":{"type":"integer"},"type":"object"},"data":{"type":"string"},"default":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"error":{"type":"string"},"filename":{"type":"string"},"help_text":{"type":"string"},"hidden":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/v1beta1.ConfigChildItem"},"type":"array","uniqueItems":false},"minimumCount":{"type":"integer"},"multi_value":{"items":{"type":"string"},"type":"array","uniqueItems":false},"multiple":{"type":"boolean"},"name":{"type":"string"},"readonly":{"type":"boolean"},"recommended":{"type":"boolean"},"repeatable":{"type":"boolean"},"required":{"type":"boolean"},"templates":{"items":{"$ref":"#/components/schemas/v1beta1.RepeatTemplate"},"type":"array","uniqueItems":false},"title":{"type":"string"},"type":{"type":"string"},"validation":{"$ref":"#/components/schemas/v1beta1.ConfigItemValidation"},"value":{"$ref":"#/components/schemas/github_com_replicatedhq_kotskinds_multitype.BoolOrString"},"valuesByGroup":{"$ref":"#/components/schemas/v1beta1.ValuesByGroup"},"when":{"type":"string"},"write_once":{"type":"boolean"}},"required":["affix","countByGroup","data","default","error","filename","help_text","hidden","items","minimumCount","multi_value","multiple","name","readonly","recommended","repeatable","required","templates","title","type","validation","value","valuesByGroup","when","write_once"],"type":"object"},"v1beta1.ConfigItemValidation":{"properties":{"regex":{"$ref":"#/components/schemas/v1beta1.RegexValidator"}},"required":["regex"],"type":"object"},"v1beta1.GroupValues":{"additionalProperties":{"type":"string"},"type":"object"},"v1beta1.RegexValidator":{"properties":{"message":{"type":"string"},"pattern":{"type":"string"}},"required":["message","pattern"],"type":"object"},"v1beta1.RepeatTemplate":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"yamlPath":{"type":"string"}},"required":["apiVersion","kind","name","namespace","yamlPath"],"type":"object"},"v1beta1.ValuesByGroup":{"additionalProperties":{"$ref":"#/components/schemas/v1beta1.GroupValues"},"type":"object"}},"securitySchemes":{"bearerauth":{"bearerFormat":"JWT","scheme":"bearer","type":"http"}}}, "info": {"contact":{"email":"support@replicated.com","name":"API Support","url":"https://github.com/replicatedhq/embedded-cluster/issues"},"description":"This is the API for the Embedded Cluster project.","license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"termsOfService":"http://swagger.io/terms/","title":"Embedded Cluster API","version":"0.1"}, "externalDocs": {"description":"OpenAPI","url":"https://swagger.io/resources/open-api/"}, "paths": {"/auth/login":{"post":{"description":"Authenticate a user","operationId":"postAuthLogin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AuthRequest"}}},"description":"Auth Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AuthResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Unauthorized"}},"summary":"Authenticate a user","tags":["auth"]}},"/console/available-network-interfaces":{"get":{"description":"List available network interfaces","operationId":"getConsoleListAvailableNetworkInterfaces","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.GetListAvailableNetworkInterfacesResponse"}}},"description":"OK"}},"summary":"List available network interfaces","tags":["console"]}},"/health":{"get":{"description":"get the health of the API","operationId":"getHealth","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/github_com_replicatedhq_embedded-cluster_api_types.Health"}}},"description":"OK"}},"summary":"Get the health of the API","tags":["health"]}},"/kubernetes/install/app-preflights/run":{"post":{"description":"Run install app preflight checks using current app configuration","operationId":"postKubernetesInstallRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run install app preflight checks","tags":["kubernetes-install"]}},"/kubernetes/install/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for install","operationId":"getKubernetesInstallAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for install","tags":["kubernetes-install"]}},"/kubernetes/install/app/config/template":{"post":{"description":"Template the app config with provided values and return the templated config","operationId":"postKubernetesInstallTemplateAppConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template the app config with provided values","tags":["kubernetes-install"]}},"/kubernetes/install/app/config/values":{"get":{"description":"Get the current app config values","operationId":"getKubernetesInstallAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values","tags":["kubernetes-install"]},"patch":{"description":"Set the app config values with partial updates","operationId":"patchKubernetesInstallAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values","tags":["kubernetes-install"]}},"/kubernetes/install/app/install":{"post":{"description":"Install the app using current configuration","operationId":"postKubernetesInstallApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppRequest"}}},"description":"Install App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Install the app","tags":["kubernetes-install"]}},"/kubernetes/install/app/status":{"get":{"description":"Get the current status of app installation","operationId":"getKubernetesInstallAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app install status","tags":["kubernetes-install"]}},"/kubernetes/install/infra/setup":{"post":{"description":"Setup infra components","operationId":"postKubernetesInstallSetupInfra","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Setup infra components","tags":["kubernetes-install"]}},"/kubernetes/install/infra/status":{"get":{"description":"Get the current status of the infra","operationId":"getKubernetesInstallInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra","tags":["kubernetes-install"]}},"/kubernetes/install/installation/config":{"get":{"description":"get the Kubernetes installation config","operationId":"getKubernetesInstallInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KubernetesInstallationConfigResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the Kubernetes installation config","tags":["kubernetes-install"]}},"/kubernetes/install/installation/configure":{"post":{"description":"configure the Kubernetes installation for install","operationId":"postKubernetesInstallConfigureInstallation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KubernetesInstallationConfig"}}},"description":"Installation config","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Configure the Kubernetes installation for install","tags":["kubernetes-install"]}},"/kubernetes/install/installation/status":{"get":{"description":"Get the current status of the installation configuration for install","operationId":"getKubernetesInstallInstallationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get installation configuration status for install","tags":["kubernetes-install"]}},"/kubernetes/upgrade/app-preflights/run":{"post":{"description":"Run upgrade app preflight checks using current app configuration","operationId":"postKubernetesUpgradeRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run upgrade app preflight checks","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for upgrade","operationId":"getKubernetesUpgradeAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for upgrade","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app/config/template":{"post":{"description":"Template the app configuration with values for upgrade","operationId":"postKubernetesUpgradeAppConfigTemplate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template app config for upgrade","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app/config/values":{"get":{"description":"Get the current app config values for upgrade","operationId":"getKubernetesUpgradeAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values for upgrade","tags":["kubernetes-upgrade"]},"patch":{"description":"Set the app config values with partial updates for upgrade","operationId":"patchKubernetesUpgradeAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values for upgrade","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app/status":{"get":{"description":"Get the current status of app upgrade","operationId":"getKubernetesUpgradeAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppUpgrade"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app upgrade status","tags":["kubernetes-upgrade"]}},"/kubernetes/upgrade/app/upgrade":{"post":{"description":"Upgrade the app using current configuration","operationId":"postKubernetesUpgradeApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppRequest"}}},"description":"Upgrade App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppUpgrade"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Upgrade the app","tags":["kubernetes-upgrade"]}},"/linux/install/airgap/process":{"post":{"description":"Process the airgap bundle for install","operationId":"postLinuxInstallProcessAirgap","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Airgap"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Process the airgap bundle","tags":["linux-install"]}},"/linux/install/airgap/status":{"get":{"description":"Get the current status of the airgap processing for install","operationId":"getLinuxInstallAirgapStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Airgap"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the airgap processing","tags":["linux-install"]}},"/linux/install/app-preflights/run":{"post":{"description":"Run install app preflight checks using current app configuration","operationId":"postLinuxInstallRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run install app preflight checks","tags":["linux-install"]}},"/linux/install/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for install","operationId":"getLinuxInstallAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for install","tags":["linux-install"]}},"/linux/install/app/config/template":{"post":{"description":"Template the app config with provided values and return the templated config","operationId":"postLinuxInstallTemplateAppConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template the app config with provided values","tags":["linux-install"]}},"/linux/install/app/config/values":{"get":{"description":"Get the current app config values","operationId":"getLinuxInstallAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values","tags":["linux-install"]},"patch":{"description":"Set the app config values with partial updates","operationId":"patchLinuxInstallAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values","tags":["linux-install"]}},"/linux/install/app/install":{"post":{"description":"Install the app using current configuration","operationId":"postLinuxInstallApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallAppRequest"}}},"description":"Install App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Install the app","tags":["linux-install"]}},"/linux/install/app/status":{"get":{"description":"Get the current status of app installation","operationId":"getLinuxInstallAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppInstall"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app install status","tags":["linux-install"]}},"/linux/install/host-preflights/run":{"post":{"description":"Run install host preflight checks using installation config and client-provided data","operationId":"postLinuxInstallRunHostPreflights","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PostInstallRunHostPreflightsRequest"}}},"description":"Post Install Run Host Preflights Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallHostPreflightsStatusResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Run install host preflight checks","tags":["linux-install"]}},"/linux/install/host-preflights/status":{"get":{"description":"Get the current status and results of host preflight checks for install","operationId":"getLinuxInstallHostPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.InstallHostPreflightsStatusResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get host preflight status for install","tags":["linux-install"]}},"/linux/install/infra/setup":{"post":{"description":"Setup infra components","operationId":"postLinuxInstallSetupInfra","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInfraSetupRequest"}}},"description":"Infra Setup Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Setup infra components","tags":["linux-install"]}},"/linux/install/infra/status":{"get":{"description":"Get the current status of the infra","operationId":"getLinuxInstallInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra","tags":["linux-install"]}},"/linux/install/installation/config":{"get":{"description":"get the installation config","operationId":"getLinuxInstallInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfigResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the installation config","tags":["linux-install"]}},"/linux/install/installation/configure":{"post":{"description":"configure the installation for install","operationId":"postLinuxInstallConfigureInstallation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfig"}}},"description":"Installation config","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Configure the installation for install","tags":["linux-install"]}},"/linux/install/installation/status":{"get":{"description":"Get the current status of the installation configuration for install","operationId":"getLinuxInstallInstallationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Status"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get installation configuration status for install","tags":["linux-install"]}},"/linux/kurl-migration/config":{"get":{"description":"Get the installation config extracted from kURL merged with EC defaults","operationId":"getKURLMigrationInstallationConfig","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.LinuxInstallationConfigResponse"}}},"description":"OK"}},"security":[{"bearerauth":[]}],"summary":"Get the installation config for kURL migration","tags":["kurl-migration"]}},"/linux/kurl-migration/start":{"post":{"description":"Start a migration from kURL to Embedded Cluster with the provided configuration","operationId":"postStartMigration","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.StartKURLMigrationRequest"}}},"description":"Start kURL Migration Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.StartKURLMigrationResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Conflict"}},"security":[{"bearerauth":[]}],"summary":"Start a migration from kURL to Embedded Cluster","tags":["kurl-migration"]}},"/linux/kurl-migration/status":{"get":{"description":"Get the current status and progress of the kURL migration","operationId":"getKURLMigrationStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.KURLMigrationStatusResponse"}}},"description":"OK"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Not Found"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the kURL migration","tags":["kurl-migration"]}},"/linux/upgrade/airgap/process":{"post":{"description":"Process the airgap bundle for upgrade","operationId":"postLinuxUpgradeProcessAirgap","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Airgap"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Process the airgap bundle","tags":["linux-upgrade"]}},"/linux/upgrade/airgap/status":{"get":{"description":"Get the current status of the airgap processing for upgrade","operationId":"getLinuxUpgradeAirgapStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Airgap"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the airgap processing","tags":["linux-upgrade"]}},"/linux/upgrade/app-preflights/run":{"post":{"description":"Run upgrade app preflight checks using current app configuration","operationId":"postLinuxUpgradeRunAppPreflights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Run upgrade app preflight checks","tags":["linux-upgrade"]}},"/linux/upgrade/app-preflights/status":{"get":{"description":"Get the current status and results of app preflight checks for upgrade","operationId":"getLinuxUpgradeAppPreflightsStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppPreflightsStatusResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app preflight status for upgrade","tags":["linux-upgrade"]}},"/linux/upgrade/app/config/template":{"post":{"description":"Template the app configuration with values for upgrade","operationId":"postLinuxUpgradeAppConfigTemplate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.TemplateAppConfigRequest"}}},"description":"Template App Config Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfig"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Template app config for upgrade","tags":["linux-upgrade"]}},"/linux/upgrade/app/config/values":{"get":{"description":"Get the current app config values for upgrade","operationId":"getLinuxUpgradeAppConfigValues","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get the app config values for upgrade","tags":["linux-upgrade"]},"patch":{"description":"Set the app config values with partial updates for upgrade","operationId":"patchLinuxUpgradeAppConfigValues","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.PatchAppConfigValuesRequest"}}},"description":"Patch App Config Values Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppConfigValuesResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Set the app config values for upgrade","tags":["linux-upgrade"]}},"/linux/upgrade/app/status":{"get":{"description":"Get the current status of app upgrade","operationId":"getLinuxUpgradeAppStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppUpgrade"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Get app upgrade status","tags":["linux-upgrade"]}},"/linux/upgrade/app/upgrade":{"post":{"description":"Upgrade the app using current configuration","operationId":"postLinuxUpgradeApp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.UpgradeAppRequest"}}},"description":"Upgrade App Request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.AppUpgrade"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"}},"security":[{"bearerauth":[]}],"summary":"Upgrade the app","tags":["linux-upgrade"]}},"/linux/upgrade/infra/status":{"get":{"description":"Get the current status of the infrastructure upgrade","operationId":"getLinuxUpgradeInfraStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Internal Server Error"}},"security":[{"bearerauth":[]}],"summary":"Get the status of the infra upgrade","tags":["linux-upgrade"]}},"/linux/upgrade/infra/upgrade":{"post":{"description":"Upgrade the infrastructure (k0s, addons, extensions)","operationId":"postLinuxUpgradeInfra","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.Infra"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Unauthorized"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Conflict"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/types.APIError"}}},"description":"Internal Server Error"}},"security":[{"bearerauth":[]}],"summary":"Upgrade the infrastructure","tags":["linux-upgrade"]}}}, diff --git a/api/docs/swagger.yaml b/api/docs/swagger.yaml index faceaa193a..679b1812cb 100644 --- a/api/docs/swagger.yaml +++ b/api/docs/swagger.yaml @@ -35,6 +35,17 @@ components: - logs - status type: object + types.AppComponent: + properties: + name: + description: Chart name + type: string + status: + $ref: '#/components/schemas/types.Status' + required: + - name + - status + type: object types.AppConfig: properties: groups: @@ -75,11 +86,17 @@ components: type: object types.AppInstall: properties: + components: + items: + $ref: '#/components/schemas/types.AppComponent' + type: array + uniqueItems: false logs: type: string status: $ref: '#/components/schemas/types.Status' required: + - components - logs - status type: object @@ -398,6 +415,7 @@ components: - StateSucceeded - StateFailed types.Status: + description: Uses existing Status type properties: description: type: string diff --git a/api/internal/handlers/kubernetes/install/handler.go b/api/internal/handlers/kubernetes/install/handler.go index 183f683d31..51d81e1393 100644 --- a/api/internal/handlers/kubernetes/install/handler.go +++ b/api/internal/handlers/kubernetes/install/handler.go @@ -143,6 +143,8 @@ func (h *Handler) PostRunAppPreflights(w http.ResponseWriter, r *http.Request) { return } + // TODO: support registry settings + err = h.controller.RunAppPreflights(r.Context(), appcontroller.RunAppPreflightOptions{ PreflightBinaryPath: preflightBinary, ProxySpec: h.cfg.Installation.ProxySpec(), @@ -352,7 +354,13 @@ func (h *Handler) PostInstallApp(w http.ResponseWriter, r *http.Request) { return } - err := h.controller.InstallApp(r.Context(), req.IgnoreAppPreflights) + // TODO: support registry settings + + err := h.controller.InstallApp(r.Context(), appcontroller.InstallAppOptions{ + IgnoreAppPreflights: req.IgnoreAppPreflights, + ProxySpec: h.cfg.Installation.ProxySpec(), + RegistrySettings: nil, + }) if err != nil { utils.LogError(r, err, h.logger, "failed to install app") utils.JSONError(w, r, err, h.logger) diff --git a/api/internal/handlers/kubernetes/kubernetes.go b/api/internal/handlers/kubernetes/kubernetes.go index 080d781971..8e8a4c85b1 100644 --- a/api/internal/handlers/kubernetes/kubernetes.go +++ b/api/internal/handlers/kubernetes/kubernetes.go @@ -138,6 +138,7 @@ func New(cfg types.APIConfig, opts ...Option) (*Handler, error) { upgrade.WithConfigValues(h.cfg.ConfigValues), upgrade.WithHelmClient(h.hcli), upgrade.WithKubeClient(h.kcli), + upgrade.WithMetadataClient(h.mcli), upgrade.WithPreflightRunner(h.preflightRunner), ) if err != nil { diff --git a/api/internal/handlers/linux/install/handler.go b/api/internal/handlers/linux/install/handler.go index 2b5de29415..4ddfeeaf9d 100644 --- a/api/internal/handlers/linux/install/handler.go +++ b/api/internal/handlers/linux/install/handler.go @@ -483,7 +483,19 @@ func (h *Handler) PostInstallApp(w http.ResponseWriter, r *http.Request) { return } - err := h.controller.InstallApp(r.Context(), req.IgnoreAppPreflights) + registrySettings, err := h.controller.CalculateRegistrySettings(r.Context()) + if err != nil { + utils.LogError(r, err, h.logger, "failed to calculate registry settings") + utils.JSONError(w, r, err, h.logger) + return + } + + err = h.controller.InstallApp(r.Context(), appcontroller.InstallAppOptions{ + IgnoreAppPreflights: req.IgnoreAppPreflights, + ProxySpec: h.cfg.RuntimeConfig.ProxySpec(), + RegistrySettings: registrySettings, + HostCABundlePath: h.cfg.RuntimeConfig.HostCABundlePath(), + }) if err != nil { utils.LogError(r, err, h.logger, "failed to install app") utils.JSONError(w, r, err, h.logger) diff --git a/api/internal/managers/airgap/process.go b/api/internal/managers/airgap/process.go index 7ce0ee4016..9ed7b34e6b 100644 --- a/api/internal/managers/airgap/process.go +++ b/api/internal/managers/airgap/process.go @@ -24,9 +24,9 @@ func (m *airgapManager) Process(ctx context.Context, registrySettings *types.Reg err := kotscli.PushImages(kotscli.PushImagesOptions{ AirgapBundle: m.airgapBundle, - RegistryAddress: registrySettings.Address, - RegistryUsername: registrySettings.Username, - RegistryPassword: registrySettings.Password, + RegistryAddress: registrySettings.LocalRegistryAddress, + RegistryUsername: registrySettings.LocalRegistryUsername, + RegistryPassword: registrySettings.LocalRegistryPassword, ClusterID: m.clusterID, Stdout: m.newLogWriter(), }) diff --git a/api/internal/managers/app/install/install.go b/api/internal/managers/app/install/install.go index 2a3e30dc0a..9d75b0e967 100644 --- a/api/internal/managers/app/install/install.go +++ b/api/internal/managers/app/install/install.go @@ -4,159 +4,136 @@ import ( "context" "fmt" "os" + "runtime/debug" - "github.com/replicatedhq/embedded-cluster/api/internal/utils" - kotscli "github.com/replicatedhq/embedded-cluster/cmd/installer/kotscli" - "github.com/replicatedhq/embedded-cluster/pkg/netutils" + "github.com/replicatedhq/embedded-cluster/api/types" + "github.com/replicatedhq/embedded-cluster/pkg/helm" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - kyaml "sigs.k8s.io/yaml" ) -// Install installs the app with the provided config values -func (m *appInstallManager) Install(ctx context.Context, configValues kotsv1beta1.ConfigValues) error { - license := &kotsv1beta1.License{} - if err := kyaml.Unmarshal(m.license, license); err != nil { - return fmt.Errorf("parse license: %w", err) +// Install installs the app with the provided Helm charts +func (m *appInstallManager) Install(ctx context.Context, installableCharts []types.InstallableHelmChart, registrySettings *types.RegistrySettings, hostCABundlePath string) error { + if err := m.setupClients(); err != nil { + return fmt.Errorf("setup clients: %w", err) } - if err := m.initKubeClient(); err != nil { - return fmt.Errorf("init kube client: %w", err) + // Start the namespace reconciler to ensure image pull secrets and other required resources in app namespaces + nsReconciler, err := runNamespaceReconciler(ctx, m.kcli, m.mcli, registrySettings, hostCABundlePath, m.logger) + if err != nil { + return fmt.Errorf("start namespace reconciler: %w", err) } + defer nsReconciler.Stop() kotsadmNamespace, err := runtimeconfig.KotsadmNamespace(ctx, m.kcli) if err != nil { return fmt.Errorf("get kotsadm namespace: %w", err) } - // Create or update secret with config values before installing - if err := m.createConfigValuesSecret(ctx, configValues, kotsadmNamespace); err != nil { - return fmt.Errorf("creating config values secret: %w", err) + // Initialize components for tracking + if err := m.initializeComponents(installableCharts); err != nil { + return fmt.Errorf("initialize components: %w", err) } - ecDomains := utils.GetDomains(m.releaseData) - - installOpts := kotscli.InstallOptions{ - AppSlug: license.Spec.AppSlug, - License: m.license, - Namespace: kotsadmNamespace, - ClusterID: m.clusterID, - AirgapBundle: m.airgapBundle, - // Skip running the KOTS app preflights in the Admin Console; they run in the manager experience installer when ENABLE_V3 is enabled - SkipPreflights: true, - // Skip pushing images to the registry since we do it separately earlier in the install process - DisableImagePush: true, - ReplicatedAppEndpoint: netutils.MaybeAddHTTPS(ecDomains.ReplicatedAppDomain), - Stdout: m.newLogWriter(), + // Install Helm charts + if err := m.installHelmCharts(ctx, installableCharts, kotsadmNamespace); err != nil { + return fmt.Errorf("install helm charts: %w", err) } - configValuesFile, err := m.createConfigValuesFile(configValues) - if err != nil { - return fmt.Errorf("creating config values file: %w", err) - } - installOpts.ConfigValuesFile = configValuesFile + return nil +} + +func (m *appInstallManager) installHelmCharts(ctx context.Context, installableCharts []types.InstallableHelmChart, kotsadmNamespace string) error { + logFn := m.logFn("app") - if m.kotsCLI != nil { - return m.kotsCLI.Install(installOpts) + if len(installableCharts) == 0 { + return fmt.Errorf("no helm charts found") } - return kotscli.Install(installOpts) -} + logFn("installing %d helm charts", len(installableCharts)) -// createConfigValuesFile creates a temporary file with the config values -func (m *appInstallManager) createConfigValuesFile(configValues kotsv1beta1.ConfigValues) (string, error) { - // Use Kubernetes-specific YAML serialization to properly handle TypeMeta and ObjectMeta - data, err := kyaml.Marshal(configValues) - if err != nil { - return "", fmt.Errorf("marshal config values: %w", err) - } + for _, installableChart := range installableCharts { + chartName := getChartDisplayName(installableChart) + logFn("installing %s chart", chartName) - configValuesFile, err := os.CreateTemp("", "config-values*.yaml") - if err != nil { - return "", fmt.Errorf("create temp file: %w", err) - } - defer configValuesFile.Close() + if err := m.installHelmChart(ctx, installableChart, kotsadmNamespace); err != nil { + return fmt.Errorf("install %s helm chart: %w", chartName, err) + } - if _, err := configValuesFile.Write(data); err != nil { - _ = os.Remove(configValuesFile.Name()) - return "", fmt.Errorf("write config values to temp file: %w", err) + logFn("successfully installed %s chart", chartName) } - return configValuesFile.Name(), nil + logFn("successfully installed all %d helm charts", len(installableCharts)) + + return nil } -// createConfigValuesSecret creates or updates a Kubernetes secret with the config values. -// TODO: Handle 1MB size limitation by storing large file data fields as pointers to other secrets -// TODO: Consider maintaining history of config values for potential rollbacks -func (m *appInstallManager) createConfigValuesSecret(ctx context.Context, configValues kotsv1beta1.ConfigValues, namespace string) error { - // Get app slug and version from release data - license := &kotsv1beta1.License{} - if err := kyaml.Unmarshal(m.license, license); err != nil { - return fmt.Errorf("parse license: %w", err) - } +func (m *appInstallManager) installHelmChart(ctx context.Context, installableChart types.InstallableHelmChart, kotsadmNamespace string) (finalErr error) { + chartName := getChartDisplayName(installableChart) - if m.releaseData == nil || m.releaseData.ChannelRelease == nil { - return fmt.Errorf("release data is required for secret creation") + if err := m.setComponentStatus(chartName, types.StateRunning, "Installing"); err != nil { + return fmt.Errorf("set component status: %w", err) } - // Marshal config values to YAML - data, err := kyaml.Marshal(configValues) + defer func() { + if r := recover(); r != nil { + finalErr = fmt.Errorf("recovered from panic: %v: %s", r, string(debug.Stack())) + } + + if finalErr != nil { + if err := m.setComponentStatus(chartName, types.StateFailed, finalErr.Error()); err != nil { + m.logger.WithError(err).Errorf("failed to set %s chart failed status", chartName) + } + } else { + if err := m.setComponentStatus(chartName, types.StateSucceeded, ""); err != nil { + m.logger.WithError(err).Errorf("failed to set %s chart succeeded status", chartName) + } + } + }() + + // Write chart archive to temp file + chartPath, err := m.writeChartArchiveToTemp(installableChart.Archive) if err != nil { - return fmt.Errorf("marshal config values: %w", err) + return fmt.Errorf("write chart archive to temp: %w", err) } + defer os.Remove(chartPath) - secretName := fmt.Sprintf("%s-config-values", license.Spec.AppSlug) - - // Create secret object - secret := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": license.Spec.AppSlug, - "app.kubernetes.io/version": m.releaseData.ChannelRelease.VersionLabel, - "app.kubernetes.io/component": "config", - "app.kubernetes.io/part-of": "embedded-cluster", - "app.kubernetes.io/managed-by": "embedded-cluster-installer", - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "config-values.yaml": data, - }, + // Fallback to admin console namespace if namespace is not set + namespace := installableChart.CR.GetNamespace() + if namespace == "" { + namespace = kotsadmNamespace } - // Try to create the secret - if err := m.kcli.Create(ctx, secret); err != nil { - if !apierrors.IsAlreadyExists(err) { - return fmt.Errorf("create config values secret: %w", err) - } - - // Secret exists, get and update it - existingSecret := &corev1.Secret{} - if err := m.kcli.Get(ctx, client.ObjectKey{ - Name: secretName, - Namespace: namespace, - }, existingSecret); err != nil { - return fmt.Errorf("get existing config values secret: %w", err) - } + // Install chart using Helm client with pre-processed values + _, err = m.hcli.Install(ctx, helm.InstallOptions{ + ChartPath: chartPath, + Namespace: namespace, + ReleaseName: installableChart.CR.GetReleaseName(), + Values: installableChart.Values, + LogFn: m.logFn("helm"), + }) + if err != nil { + return err // do not wrap as wrapping is repetitive, e.g. "helm install: helm install: context deadline exceeded" + } - // Update the existing secret's data and labels - existingSecret.Data = secret.Data - existingSecret.Labels = secret.Labels + return nil +} - if err := m.kcli.Update(ctx, existingSecret); err != nil { - return fmt.Errorf("update config values secret: %w", err) - } +// initializeComponents initializes the component tracking with chart names +func (m *appInstallManager) initializeComponents(charts []types.InstallableHelmChart) error { + chartNames := make([]string, 0, len(charts)) + for _, chart := range charts { + chartNames = append(chartNames, getChartDisplayName(chart)) } - return nil + return m.appInstallStore.RegisterComponents(chartNames) +} + +// getChartDisplayName returns the name of the chart for display purposes. It prefers the +// metadata.name field if available and falls back to the chart name. +func getChartDisplayName(chart types.InstallableHelmChart) string { + chartName := chart.CR.GetName() + if chartName == "" { + chartName = chart.CR.GetChartName() + } + return chartName } diff --git a/api/internal/managers/app/install/install_test.go b/api/internal/managers/app/install/install_test.go index 19b291ca9d..4f4c2714dd 100644 --- a/api/internal/managers/app/install/install_test.go +++ b/api/internal/managers/app/install/install_test.go @@ -1,25 +1,32 @@ package install import ( - "context" + "archive/tar" + "bytes" + "compress/gzip" + "encoding/base64" + "errors" "fmt" "os" "testing" + "time" + appinstallstore "github.com/replicatedhq/embedded-cluster/api/internal/store/app/install" "github.com/replicatedhq/embedded-cluster/api/pkg/logger" - kotscli "github.com/replicatedhq/embedded-cluster/cmd/installer/kotscli" + "github.com/replicatedhq/embedded-cluster/api/types" + "github.com/replicatedhq/embedded-cluster/pkg/helm" "github.com/replicatedhq/embedded-cluster/pkg/release" kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + kotsv1beta2 "github.com/replicatedhq/kotskinds/apis/kots/v1beta2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" + metadatafake "k8s.io/client-go/metadata/fake" "sigs.k8s.io/controller-runtime/pkg/client" - clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/client/interceptor" + "sigs.k8s.io/controller-runtime/pkg/client/fake" kyaml "sigs.k8s.io/yaml" ) @@ -27,6 +34,9 @@ func TestAppInstallManager_Install(t *testing.T) { // Setup environment variable for V3 t.Setenv("ENABLE_V3", "1") + // Create valid helm chart archive + mockChartArchive := createTestChartArchive(t, "test-chart", "0.1.0") + // Create test license license := &kotsv1beta1.License{ Spec: kotsv1beta1.LicenseSpec{ @@ -36,8 +46,9 @@ func TestAppInstallManager_Install(t *testing.T) { licenseBytes, err := kyaml.Marshal(license) require.NoError(t, err) - // Create test release data + // Create test release data with helm chart archives releaseData := &release.ReleaseData{ + HelmChartArchives: [][]byte{mockChartArchive}, ChannelRelease: &release.ChannelRelease{ DefaultDomains: release.Domains{ ReplicatedAppDomain: "replicated.app", @@ -51,538 +62,427 @@ func TestAppInstallManager_Install(t *testing.T) { }) require.NoError(t, err) - t.Run("Config values should be passed to the installer", func(t *testing.T) { - configValues := kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "key1": { - Value: "value1", - }, - "key2": { - Value: "value2", - }, + // create fake kube client with kotsadm namespace + kotsadmNamespace := &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + }, + } + fakeKcli := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(kotsadmNamespace).Build() + + t.Run("Success", func(t *testing.T) { + // Create InstallableHelmCharts with weights - should already be sorted at this stage + installableCharts := []types.InstallableHelmChart{ + createTestInstallableHelmChart(t, "nginx-chart", "1.0.0", "web-server", "web", 10, map[string]any{ + "image": map[string]any{ + "repository": "nginx", + "tag": "latest", }, - }, + "replicas": 3, + }), + createTestInstallableHelmChart(t, "postgres-chart", "2.0.0", "database", "data", 20, map[string]any{ + "database": map[string]any{ + "host": "postgres.example.com", + "password": "secret", + }, + }), } - // Create mock installer with detailed verification - mockKotsCLI := &kotscli.MockKotsCLI{} - mockKotsCLI.On("Install", mock.MatchedBy(func(opts kotscli.InstallOptions) bool { - // Verify basic install options - if opts.AppSlug != "test-app" { - t.Logf("AppSlug mismatch: expected 'test-app', got '%s'", opts.AppSlug) - return false - } - if opts.License == nil { - t.Logf("License is nil") - return false - } - if opts.Namespace != "test-app" { - t.Logf("Namespace mismatch: expected 'test-app', got '%s'", opts.Namespace) - return false - } - if opts.ClusterID != "test-cluster" { - t.Logf("ClusterID mismatch: expected 'test-cluster', got '%s'", opts.ClusterID) - return false - } - if opts.AirgapBundle != "test-airgap.tar.gz" { - t.Logf("AirgapBundle mismatch: expected 'test-airgap.tar.gz', got '%s'", opts.AirgapBundle) - return false - } - if opts.ReplicatedAppEndpoint == "" { - t.Logf("ReplicatedAppEndpoint is empty") + // Create registry settings for testing image pull secret creation + dockerConfigJSON := `{"auths":{"registry.example.com":{"auth":"dXNlcjpwYXNz"}}}` + registrySettings := &types.RegistrySettings{ + ImagePullSecretName: "test-pull-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + } + + // Create a temp CA bundle file for testing + caContent := "-----BEGIN CERTIFICATE-----\ntest-ca-content\n-----END CERTIFICATE-----" + tmpCAFile, err := os.CreateTemp("", "ca-bundle-*.crt") + require.NoError(t, err) + defer os.Remove(tmpCAFile.Name()) + _, err = tmpCAFile.WriteString(caContent) + require.NoError(t, err) + tmpCAFile.Close() + + // Create fake metadata client for CA configmap creation + fakeMcli := metadatafake.NewSimpleMetadataClient(metadatafake.NewTestScheme()) + + // Create mock helm client that validates pre-processed values + mockHelmClient := &helm.MockClient{} + + // Chart 1 installation (nginx chart) + nginxCall := mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + if opts.ReleaseName != "web-server" { return false } - if opts.ConfigValuesFile == "" { - t.Logf("ConfigValuesFile is empty") + if opts.Namespace != "web" { return false } - if !opts.DisableImagePush { - t.Logf("DisableImagePush is false") - return false + // Check if values contain expected pre-processed data for nginx chart + if vals, ok := opts.Values["image"].(map[string]any); ok { + return vals["repository"] == "nginx" && vals["tag"] == "latest" && opts.Values["replicas"] == 3 } + return false + })).Return("Release \"web-server\" has been installed.", nil) - // Verify config values file content - b, err := os.ReadFile(opts.ConfigValuesFile) - if err != nil { - t.Logf("Failed to read config values file: %v", err) - return false - } - var cv kotsv1beta1.ConfigValues - if err := kyaml.Unmarshal(b, &cv); err != nil { - t.Logf("Failed to unmarshal config values: %v", err) + // Chart 2 installation (database chart) + databaseCall := mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + if opts.ReleaseName != "database" { return false } - if cv.Spec.Values["key1"].Value != "value1" { - t.Logf("Config value key1 mismatch: expected 'value1', got '%s'", cv.Spec.Values["key1"].Value) + if opts.Namespace != "data" { return false } - if cv.Spec.Values["key2"].Value != "value2" { - t.Logf("Config value key2 mismatch: expected 'value2', got '%s'", cv.Spec.Values["key2"].Value) - return false + // Check if values contain expected pre-processed database data + if vals, ok := opts.Values["database"].(map[string]any); ok { + return vals["host"] == "postgres.example.com" && vals["password"] == "secret" } - return true - })).Return(nil) + return false + })).Return("Release \"database\" has been installed.", nil) - // Create fake kube client - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - fakeKcli := clientfake.NewClientBuilder().WithScheme(sch).Build() + // Verify installation order + mock.InOrder( + nginxCall, + databaseCall, + ) // Create manager manager, err := NewAppInstallManager( - WithLicense(licenseBytes), WithClusterID("test-cluster"), WithAirgapBundle("test-airgap.tar.gz"), WithReleaseData(releaseData), - WithKotsCLI(mockKotsCLI), + WithLicense(licenseBytes), + WithHelmClient(mockHelmClient), + WithLogger(logger.NewDiscardLogger()), + WithKubeClient(fakeKcli), + WithMetadataClient(fakeMcli), + ) + require.NoError(t, err) + + // Run installation with registry settings and host CA bundle path + err = manager.Install(t.Context(), installableCharts, registrySettings, tmpCAFile.Name()) + require.NoError(t, err) + + mockHelmClient.AssertExpectations(t) + + // Verify image pull secret was created in the app namespace + secret := &corev1.Secret{} + err = fakeKcli.Get(t.Context(), client.ObjectKey{ + Namespace: "test-app", + Name: "test-pull-secret", + }, secret) + require.NoError(t, err) + assert.Equal(t, corev1.SecretTypeDockerConfigJson, secret.Type) + assert.Equal(t, dockerConfigJSON, string(secret.Data[".dockerconfigjson"])) + + // Verify CA configmap was created in the app namespace + configMap := &corev1.ConfigMap{} + err = fakeKcli.Get(t.Context(), client.ObjectKey{ + Namespace: "test-app", + Name: "kotsadm-private-cas", + }, configMap) + require.NoError(t, err) + assert.Contains(t, configMap.Data["ca_0.crt"], "test-ca-content") + }) + + t.Run("Install updates status correctly", func(t *testing.T) { + installableCharts := []types.InstallableHelmChart{ + createTestInstallableHelmChart(t, "monitoring-chart", "1.0.0", "prometheus", "monitoring", 0, map[string]any{"key": "value"}), + } + + // Create mock helm client + mockHelmClient := &helm.MockClient{} + mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + return opts.ChartPath != "" && opts.ReleaseName == "prometheus" && opts.Namespace == "monitoring" + })).Return("Release \"prometheus\" has been installed.", nil) + + // Create manager with initialized store + store := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ + Status: types.Status{State: types.StatePending}, + })) + manager, err := NewAppInstallManager( + WithClusterID("test-cluster"), + WithReleaseData(releaseData), + WithLicense(licenseBytes), + WithHelmClient(mockHelmClient), WithLogger(logger.NewDiscardLogger()), + WithAppInstallStore(store), WithKubeClient(fakeKcli), ) require.NoError(t, err) + // Verify initial status + appInstall, err := manager.GetStatus() + require.NoError(t, err) + assert.Equal(t, types.StatePending, appInstall.Status.State) + // Run installation - err = manager.Install(context.Background(), configValues) + err = manager.Install(t.Context(), installableCharts, nil, "") + require.NoError(t, err) + + // Verify components status + appInstall, err = manager.GetStatus() require.NoError(t, err) + assert.NotEmpty(t, appInstall.Components) - // Verify mock was called - mockKotsCLI.AssertExpectations(t) + mockHelmClient.AssertExpectations(t) }) -} -func TestAppInstallManager_createConfigValuesFile(t *testing.T) { - manager := &appInstallManager{} + t.Run("Install handles errors correctly", func(t *testing.T) { + installableCharts := []types.InstallableHelmChart{ + createTestInstallableHelmChart(t, "logging-chart", "1.0.0", "fluentd", "logging", 0, map[string]any{"key": "value"}), + } + + // Create mock helm client that fails + mockHelmClient := &helm.MockClient{} + mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + return opts.ChartPath != "" && opts.ReleaseName == "fluentd" && opts.Namespace == "logging" + })).Return("", assert.AnError) - configValues := kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "testKey": { - Value: "testValue", - }, + // Create manager with initialized store + store := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ + Status: types.Status{State: types.StatePending}, + })) + manager, err := NewAppInstallManager( + WithClusterID("test-cluster"), + WithReleaseData(releaseData), + WithLicense(licenseBytes), + WithHelmClient(mockHelmClient), + WithLogger(logger.NewDiscardLogger()), + WithAppInstallStore(store), + WithKubeClient(fakeKcli), + ) + require.NoError(t, err) + + // Run installation (should fail) + err = manager.Install(t.Context(), installableCharts, nil, "") + assert.Error(t, err) + + mockHelmClient.AssertExpectations(t) + }) + + t.Run("GetStatus returns current app install state", func(t *testing.T) { + // Create test store with known status + store := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ + Status: types.Status{ + State: types.StateRunning, + Description: "Installing application", + LastUpdated: time.Now(), }, - }, - } + Logs: "Installation started\n", + })) + + // Create manager with test store + manager, err := NewAppInstallManager( + WithLogger(logger.NewDiscardLogger()), + WithAppInstallStore(store), + WithHelmClient(&helm.MockClient{}), + WithKubeClient(fakeKcli), + ) + require.NoError(t, err) - filename, err := manager.createConfigValuesFile(configValues) - assert.NoError(t, err) - assert.NotEmpty(t, filename) + // Test GetStatus + appInstall, err := manager.GetStatus() + require.NoError(t, err) + assert.Equal(t, types.StateRunning, appInstall.Status.State) + assert.Equal(t, "Installing application", appInstall.Status.Description) + assert.Equal(t, "Installation started\n", appInstall.Logs) + }) +} - // Verify file exists and contains correct content - data, err := os.ReadFile(filename) - assert.NoError(t, err) +// createTarGzArchive creates a tar.gz archive with the given files +func createTarGzArchive(t *testing.T, files map[string]string) []byte { + t.Helper() - var unmarshaled kotsv1beta1.ConfigValues - err = kyaml.Unmarshal(data, &unmarshaled) - assert.NoError(t, err) - assert.Equal(t, "testValue", unmarshaled.Spec.Values["testKey"].Value) + var buf bytes.Buffer + gw := gzip.NewWriter(&buf) + tw := tar.NewWriter(gw) - // Clean up - os.Remove(filename) + for filename, content := range files { + header := &tar.Header{ + Name: filename, + Mode: 0600, + Size: int64(len(content)), + } + require.NoError(t, tw.WriteHeader(header)) + _, err := tw.Write([]byte(content)) + require.NoError(t, err) + } + + require.NoError(t, tw.Close()) + require.NoError(t, gw.Close()) + + return buf.Bytes() } -func TestAppInstallManager_Install_ConfigValuesSecret(t *testing.T) { - // Set up environment and release data for all tests - t.Setenv("ENABLE_V3", "1") - err := release.SetReleaseDataForTests(map[string][]byte{ - "channelrelease.yaml": []byte("# channel release object\nappSlug: test-app"), +func createTestChartArchive(t *testing.T, name, version string) []byte { + chartYaml := fmt.Sprintf(`apiVersion: v2 +name: %s +version: %s +description: A test Helm chart +type: application +`, name, version) + + return createTarGzArchive(t, map[string]string{ + fmt.Sprintf("%s/Chart.yaml", name): chartYaml, }) - require.NoError(t, err) +} - tests := []struct { - name string - releaseData *release.ReleaseData - configValues kotsv1beta1.ConfigValues - setupClient func(t *testing.T) client.Client - expectError bool - expectedErrorContains string - validateSecret func(t *testing.T, kcli client.Client) - validateKotsCalled bool - }{ - { - name: "first install creates secret with multiple config values", - releaseData: &release.ReleaseData{ - ChannelRelease: &release.ChannelRelease{ - VersionLabel: "v1.0.0", - DefaultDomains: release.Domains{ - ReplicatedAppDomain: "replicated.app", - }, - }, - }, - configValues: kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "key1": {Value: "value1"}, - "key2": {Value: "value2"}, - "key3": {Value: "value3"}, - }, - }, - }, - setupClient: func(t *testing.T) client.Client { - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - return clientfake.NewClientBuilder().WithScheme(sch).Build() - }, - expectError: false, - validateSecret: func(t *testing.T, kcli client.Client) { - // Get and verify secret - secret := &corev1.Secret{} - err := kcli.Get(context.Background(), client.ObjectKey{ - Name: "test-app-config-values", - Namespace: "test-app", - }, secret) - require.NoError(t, err) - - // Verify labels - assert.Equal(t, "test-app", secret.Labels["app.kubernetes.io/name"]) - assert.Equal(t, "v1.0.0", secret.Labels["app.kubernetes.io/version"]) - assert.Equal(t, "config", secret.Labels["app.kubernetes.io/component"]) - assert.Equal(t, "embedded-cluster", secret.Labels["app.kubernetes.io/part-of"]) - assert.Equal(t, "embedded-cluster-installer", secret.Labels["app.kubernetes.io/managed-by"]) - - // Verify type - assert.Equal(t, corev1.SecretTypeOpaque, secret.Type) - - // Verify data - data, ok := secret.Data["config-values.yaml"] - require.True(t, ok) - - // Unmarshal and verify values - var cv kotsv1beta1.ConfigValues - err = kyaml.Unmarshal(data, &cv) - require.NoError(t, err) - assert.Equal(t, "value1", cv.Spec.Values["key1"].Value) - assert.Equal(t, "value2", cv.Spec.Values["key2"].Value) - assert.Equal(t, "value3", cv.Spec.Values["key3"].Value) - }, - validateKotsCalled: true, +// Helper functions to create test data that can be reused across test cases +func createTestHelmChartCR(name, releaseName, namespace string, weight int64) *kotsv1beta2.HelmChart { + return &kotsv1beta2.HelmChart{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "kots.io/v1beta2", + Kind: "HelmChart", }, - { - name: "existing secret is fetched and updated", - releaseData: &release.ReleaseData{ - ChannelRelease: &release.ChannelRelease{ - VersionLabel: "v1.0.0", - DefaultDomains: release.Domains{ - ReplicatedAppDomain: "replicated.app", - }, - }, - }, - configValues: kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "newkey": {Value: "newvalue"}, - }, - }, - }, - setupClient: func(t *testing.T) client.Client { - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - - // Create existing secret with old version - existingSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-app-config-values", - Namespace: "test-app", - Labels: map[string]string{ - "app.kubernetes.io/version": "v0.9.0", - }, - }, - Data: map[string][]byte{ - "config-values.yaml": []byte("old: data"), - }, - } - - return clientfake.NewClientBuilder(). - WithScheme(sch). - WithObjects(existingSecret). - Build() - }, - expectError: false, - validateSecret: func(t *testing.T, kcli client.Client) { - // Get and verify secret was recreated - secret := &corev1.Secret{} - err := kcli.Get(context.Background(), client.ObjectKey{ - Name: "test-app-config-values", - Namespace: "test-app", - }, secret) - require.NoError(t, err) - - // Verify updated version label - assert.Equal(t, "v1.0.0", secret.Labels["app.kubernetes.io/version"]) - - // Verify new data - data, ok := secret.Data["config-values.yaml"] - require.True(t, ok) - - var cv kotsv1beta1.ConfigValues - err = kyaml.Unmarshal(data, &cv) - require.NoError(t, err) - assert.Equal(t, "newvalue", cv.Spec.Values["newkey"].Value) - }, - validateKotsCalled: true, + ObjectMeta: metav1.ObjectMeta{ + Name: name, }, - { - name: "fails when release data is missing", - releaseData: nil, - configValues: kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "key1": {Value: "value1"}, - }, - }, - }, - setupClient: func(t *testing.T) client.Client { - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - return clientfake.NewClientBuilder().WithScheme(sch).Build() - }, - expectError: true, - expectedErrorContains: "release data is required", - validateKotsCalled: false, - }, - { - name: "fails when channel release is missing", - releaseData: &release.ReleaseData{ - ChannelRelease: nil, - }, - configValues: kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "key1": {Value: "value1"}, - }, - }, - }, - setupClient: func(t *testing.T) client.Client { - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - return clientfake.NewClientBuilder().WithScheme(sch).Build() - }, - expectError: true, - expectedErrorContains: "release data is required", - validateKotsCalled: false, - }, - { - name: "fails when get returns error", - releaseData: &release.ReleaseData{ - ChannelRelease: &release.ChannelRelease{ - VersionLabel: "v1.0.0", - DefaultDomains: release.Domains{ - ReplicatedAppDomain: "replicated.app", - }, - }, - }, - configValues: kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "key1": {Value: "value1"}, - }, - }, - }, - setupClient: func(t *testing.T) client.Client { - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - - // Create existing secret - existingSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-app-config-values", - Namespace: "test-app", - }, - } - - return clientfake.NewClientBuilder(). - WithScheme(sch). - WithObjects(existingSecret). - WithInterceptorFuncs(interceptor.Funcs{ - Get: func(ctx context.Context, c client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { - if key.Name == "test-app-config-values" { - return fmt.Errorf("simulated get error") - } - return c.Get(ctx, key, obj, opts...) - }, - }). - Build() + Spec: kotsv1beta2.HelmChartSpec{ + Chart: kotsv1beta2.ChartIdentifier{ + Name: name, + ChartVersion: "1.0.0", }, - expectError: true, - expectedErrorContains: "get existing config values secret", - validateKotsCalled: false, + ReleaseName: releaseName, + Namespace: namespace, + Weight: weight, }, - { - name: "fails when update returns error", - releaseData: &release.ReleaseData{ - ChannelRelease: &release.ChannelRelease{ - VersionLabel: "v1.0.0", - DefaultDomains: release.Domains{ - ReplicatedAppDomain: "replicated.app", - }, - }, - }, - configValues: kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "key1": {Value: "value1"}, - }, - }, - }, - setupClient: func(t *testing.T) client.Client { - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - - // Create existing secret - existingSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-app-config-values", - Namespace: "test-app", - }, - } - - return clientfake.NewClientBuilder(). - WithScheme(sch). - WithObjects(existingSecret). - WithInterceptorFuncs(interceptor.Funcs{ - Update: func(ctx context.Context, c client.WithWatch, obj client.Object, opts ...client.UpdateOption) error { - if secret, ok := obj.(*corev1.Secret); ok && secret.Name == "test-app-config-values" { - return fmt.Errorf("simulated update error") - } - return c.Update(ctx, obj, opts...) - }, - }). - Build() - }, - expectError: true, - expectedErrorContains: "update config values secret", - validateKotsCalled: false, + } +} + +func createTestInstallableHelmChart(t *testing.T, chartName, chartVersion, releaseName, namespace string, weight int64, values map[string]any) types.InstallableHelmChart { + return types.InstallableHelmChart{ + Archive: createTestChartArchive(t, chartName, chartVersion), + Values: values, + CR: createTestHelmChartCR(chartName, releaseName, namespace, weight), + } +} + +// TestComponentStatusTracking tests that components are properly initialized and tracked +func TestComponentStatusTracking(t *testing.T) { + // Create test license + license := &kotsv1beta1.License{ + Spec: kotsv1beta1.LicenseSpec{ + AppSlug: "test-app", }, - { - name: "fails when create returns non-AlreadyExists error", - releaseData: &release.ReleaseData{ - ChannelRelease: &release.ChannelRelease{ - VersionLabel: "v1.0.0", - DefaultDomains: release.Domains{ - ReplicatedAppDomain: "replicated.app", - }, - }, - }, - configValues: kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "key1": {Value: "value1"}, - }, - }, - }, - setupClient: func(t *testing.T) client.Client { - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - - return clientfake.NewClientBuilder(). - WithScheme(sch). - WithInterceptorFuncs(interceptor.Funcs{ - Create: func(ctx context.Context, c client.WithWatch, obj client.Object, opts ...client.CreateOption) error { - if _, ok := obj.(*corev1.Secret); ok { - return fmt.Errorf("simulated create error") - } - return c.Create(ctx, obj, opts...) - }, - }). - Build() - }, - expectError: true, - expectedErrorContains: "create config values secret", - validateKotsCalled: false, + } + licenseBytes, err := kyaml.Marshal(license) + require.NoError(t, err) + + // create fake kube client with kotsadm namespace + kotsadmNamespace := &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + APIVersion: "v1", }, - { - name: "handles empty config values", - releaseData: &release.ReleaseData{ - ChannelRelease: &release.ChannelRelease{ - VersionLabel: "v1.0.0", - DefaultDomains: release.Domains{ - ReplicatedAppDomain: "replicated.app", - }, - }, - }, - configValues: kotsv1beta1.ConfigValues{ - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{}, - }, - }, - setupClient: func(t *testing.T) client.Client { - sch := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(sch)) - require.NoError(t, scheme.AddToScheme(sch)) - return clientfake.NewClientBuilder().WithScheme(sch).Build() - }, - expectError: false, - validateSecret: func(t *testing.T, kcli client.Client) { - // Get and verify secret was created even with empty values - secret := &corev1.Secret{} - err := kcli.Get(context.Background(), client.ObjectKey{ - Name: "test-app-config-values", - Namespace: "test-app", - }, secret) - require.NoError(t, err) - - // Verify data exists - data, ok := secret.Data["config-values.yaml"] - require.True(t, ok) - require.NotEmpty(t, data) - }, - validateKotsCalled: true, + ObjectMeta: metav1.ObjectMeta{ + Name: "kotsadm", }, } + fakeKcli := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(kotsadmNamespace).Build() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Setup - license := &kotsv1beta1.License{ - Spec: kotsv1beta1.LicenseSpec{AppSlug: "test-app"}, - } - licenseBytes, err := kyaml.Marshal(license) - require.NoError(t, err) + t.Run("Components are registered and status is tracked", func(t *testing.T) { + // Create test charts with different weights + installableCharts := []types.InstallableHelmChart{ + createTestInstallableHelmChart(t, "database-chart", "1.0.0", "postgres", "data", 10, map[string]any{"key": "value1"}), + createTestInstallableHelmChart(t, "web-chart", "2.0.0", "nginx", "web", 20, map[string]any{"key": "value2"}), + } - mockKotsCLI := &kotscli.MockKotsCLI{} - if tt.validateKotsCalled { - mockKotsCLI.On("Install", mock.Anything).Return(nil) - } + // Create mock helm client + mockHelmClient := &helm.MockClient{} - kcli := tt.setupClient(t) - - manager, err := NewAppInstallManager( - WithLicense(licenseBytes), - WithClusterID("test-cluster"), - WithAirgapBundle("test-airgap.tar.gz"), - WithReleaseData(tt.releaseData), - WithKotsCLI(mockKotsCLI), - WithLogger(logger.NewDiscardLogger()), - WithKubeClient(kcli), - ) - require.NoError(t, err) - - // Execute - err = manager.Install(context.Background(), tt.configValues) - - // Verify - if tt.expectError { - require.Error(t, err) - assert.Contains(t, err.Error(), tt.expectedErrorContains) - } else { - require.NoError(t, err) - if tt.validateSecret != nil { - tt.validateSecret(t, kcli) - } - } + // Database chart installation (should be first due to lower weight) + mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + return opts.ReleaseName == "postgres" && opts.Namespace == "data" + })).Return("Release \"postgres\" has been installed.", nil).Once() - if tt.validateKotsCalled { - mockKotsCLI.AssertExpectations(t) - } else { - mockKotsCLI.AssertNotCalled(t, "Install") - } - }) - } + // Web chart installation (should be second due to higher weight) + mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + return opts.ReleaseName == "nginx" && opts.Namespace == "web" + })).Return("Release \"nginx\" has been installed.", nil).Once() + + // Create manager with in-memory store + appInstallStore := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ + Status: types.Status{State: types.StatePending}, + })) + manager, err := NewAppInstallManager( + WithAppInstallStore(appInstallStore), + WithReleaseData(&release.ReleaseData{}), + WithLicense(licenseBytes), + WithClusterID("test-cluster"), + WithHelmClient(mockHelmClient), + WithKubeClient(fakeKcli), + ) + require.NoError(t, err) + + // Install the charts + err = manager.Install(t.Context(), installableCharts, nil, "") + require.NoError(t, err) + + // Verify that components were registered and have correct status + appInstall, err := manager.GetStatus() + require.NoError(t, err) + + // Should have 2 components + assert.Len(t, appInstall.Components, 2, "Should have 2 components") + + // Components should be sorted by weight (database first with weight 10, web second with weight 20) + assert.Equal(t, "database-chart", appInstall.Components[0].Name) + assert.Equal(t, types.StateSucceeded, appInstall.Components[0].Status.State) + + assert.Equal(t, "web-chart", appInstall.Components[1].Name) + assert.Equal(t, types.StateSucceeded, appInstall.Components[1].Status.State) + + mockHelmClient.AssertExpectations(t) + }) + + t.Run("Component failure is tracked correctly", func(t *testing.T) { + // Create test chart + installableCharts := []types.InstallableHelmChart{ + createTestInstallableHelmChart(t, "failing-chart", "1.0.0", "failing-app", "default", 0, map[string]any{"key": "value"}), + } + + // Create mock helm client that fails + mockHelmClient := &helm.MockClient{} + mockHelmClient.On("Install", mock.Anything, mock.MatchedBy(func(opts helm.InstallOptions) bool { + return opts.ReleaseName == "failing-app" + })).Return("", errors.New("helm install failed")) + + // Create manager with in-memory store + appInstallStore := appinstallstore.NewMemoryStore(appinstallstore.WithAppInstall(types.AppInstall{ + Status: types.Status{State: types.StatePending}, + })) + manager, err := NewAppInstallManager( + WithAppInstallStore(appInstallStore), + WithReleaseData(&release.ReleaseData{}), + WithLicense(licenseBytes), + WithClusterID("test-cluster"), + WithHelmClient(mockHelmClient), + WithKubeClient(fakeKcli), + ) + require.NoError(t, err) + + // Install the charts (should fail) + err = manager.Install(t.Context(), installableCharts, nil, "") + require.Error(t, err) + + // Verify that component failure is tracked + appInstall, err := manager.GetStatus() + require.NoError(t, err) + + // Should have 1 component + assert.Len(t, appInstall.Components, 1, "Should have 1 component") + + // Component should be marked as failed + failedComponent := appInstall.Components[0] + assert.Equal(t, "failing-chart", failedComponent.Name) + assert.Equal(t, types.StateFailed, failedComponent.Status.State) + assert.Contains(t, failedComponent.Status.Description, "helm install failed") + + mockHelmClient.AssertExpectations(t) + }) } diff --git a/api/internal/managers/app/install/manager.go b/api/internal/managers/app/install/manager.go index acf52b4263..462e8e2e77 100644 --- a/api/internal/managers/app/install/manager.go +++ b/api/internal/managers/app/install/manager.go @@ -5,11 +5,12 @@ import ( appinstallstore "github.com/replicatedhq/embedded-cluster/api/internal/store/app/install" "github.com/replicatedhq/embedded-cluster/api/pkg/logger" - kotscli "github.com/replicatedhq/embedded-cluster/cmd/installer/kotscli" + "github.com/replicatedhq/embedded-cluster/api/types" + "github.com/replicatedhq/embedded-cluster/pkg/helm" "github.com/replicatedhq/embedded-cluster/pkg/release" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" "github.com/sirupsen/logrus" helmcli "helm.sh/helm/v3/pkg/cli" + "k8s.io/client-go/metadata" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -17,8 +18,8 @@ var _ AppInstallManager = &appInstallManager{} // AppInstallManager provides methods for managing app installation type AppInstallManager interface { - // Install installs the app with the provided config values - Install(ctx context.Context, configValues kotsv1beta1.ConfigValues) error + // Install installs the app with the provided Helm charts + Install(ctx context.Context, installableCharts []types.InstallableHelmChart, registrySettings *types.RegistrySettings, hostCABundlePath string) error } // appInstallManager is an implementation of the AppInstallManager interface @@ -28,8 +29,9 @@ type appInstallManager struct { license []byte clusterID string airgapBundle string - kotsCLI kotscli.KotsCLI + hcli helm.Client kcli client.Client + mcli metadata.Interface kubernetesEnvSettings *helmcli.EnvSettings logger logrus.FieldLogger } @@ -72,9 +74,9 @@ func WithAirgapBundle(airgapBundle string) AppInstallManagerOption { } } -func WithKotsCLI(kotsCLI kotscli.KotsCLI) AppInstallManagerOption { +func WithHelmClient(hcli helm.Client) AppInstallManagerOption { return func(m *appInstallManager) { - m.kotsCLI = kotsCLI + m.hcli = hcli } } @@ -84,6 +86,12 @@ func WithKubeClient(kcli client.Client) AppInstallManagerOption { } } +func WithMetadataClient(mcli metadata.Interface) AppInstallManagerOption { + return func(m *appInstallManager) { + m.mcli = mcli + } +} + func WithKubernetesEnvSettings(envSettings *helmcli.EnvSettings) AppInstallManagerOption { return func(m *appInstallManager) { m.kubernetesEnvSettings = envSettings diff --git a/api/internal/managers/app/install/mock.go b/api/internal/managers/app/install/mock.go index 129b0f6176..1b4b95d037 100644 --- a/api/internal/managers/app/install/mock.go +++ b/api/internal/managers/app/install/mock.go @@ -4,7 +4,6 @@ import ( "context" "github.com/replicatedhq/embedded-cluster/api/types" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" "github.com/stretchr/testify/mock" ) @@ -14,13 +13,7 @@ type MockAppInstallManager struct { } // Install mocks the Install method -func (m *MockAppInstallManager) Install(ctx context.Context, configValues kotsv1beta1.ConfigValues) error { - args := m.Called(ctx, configValues) +func (m *MockAppInstallManager) Install(ctx context.Context, installableCharts []types.InstallableHelmChart, registrySettings *types.RegistrySettings, hostCABundlePath string) error { + args := m.Called(ctx, installableCharts, registrySettings, hostCABundlePath) return args.Error(0) } - -// GetStatus mocks the GetStatus method -func (m *MockAppInstallManager) GetStatus() (types.AppInstall, error) { - args := m.Called() - return args.Get(0).(types.AppInstall), args.Error(1) -} diff --git a/api/internal/managers/app/install/namespaces_reconciler.go b/api/internal/managers/app/install/namespaces_reconciler.go new file mode 100644 index 0000000000..74894f8cf5 --- /dev/null +++ b/api/internal/managers/app/install/namespaces_reconciler.go @@ -0,0 +1,228 @@ +package install + +import ( + "context" + "encoding/base64" + "fmt" + "slices" + "time" + + "github.com/replicatedhq/embedded-cluster/api/types" + "github.com/replicatedhq/embedded-cluster/pkg/addons/adminconsole" + addonstypes "github.com/replicatedhq/embedded-cluster/pkg/addons/types" + "github.com/replicatedhq/embedded-cluster/pkg/release" + "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" + "github.com/sirupsen/logrus" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/metadata" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + reconcileInterval = 5 * time.Second +) + +// NamespaceReconciler handles ensuring image pull secrets and CA configmaps in app namespaces. +// It reads additionalNamespaces from the Application CR, ensures secrets and configmaps exist +// in those namespaces plus the kotsadm namespace, and polls for new namespace +// creation to deploy resources to them. +type NamespaceReconciler struct { + kcli client.Client + mcli metadata.Interface + registrySettings *types.RegistrySettings + hostCABundlePath string + logger logrus.FieldLogger + + watchedNamespaces []string + cancel context.CancelFunc +} + +// runNamespaceReconciler creates and starts a reconciler that: +// 1. Reads additionalNamespaces from release.GetApplication() +// 2. Immediately ensures image pull secrets and other resources in all watched namespaces +// 3. Starts background polling to reconcile namespaces periodically +// Returns a cancellable namespace reconciler instance. +func runNamespaceReconciler( + ctx context.Context, + kcli client.Client, + mcli metadata.Interface, + registrySettings *types.RegistrySettings, + hostCABundlePath string, + logger logrus.FieldLogger, +) (*NamespaceReconciler, error) { + // Get kotsadm namespace + kotsadmNamespace, err := runtimeconfig.KotsadmNamespace(ctx, kcli) + if err != nil { + return nil, fmt.Errorf("get kotsadm namespace: %w", err) + } + + // Get watched namespaces from Application CR + watchedNamespaces := []string{kotsadmNamespace} + if app := release.GetApplication(); app != nil { + watchedNamespaces = append(watchedNamespaces, app.Spec.AdditionalNamespaces...) + } + + ctx, cancel := context.WithCancel(ctx) + + r := &NamespaceReconciler{ + kcli: kcli, + mcli: mcli, + registrySettings: registrySettings, + hostCABundlePath: hostCABundlePath, + logger: logger, + watchedNamespaces: watchedNamespaces, + cancel: cancel, + } + + // Immediately reconcile all namespaces + r.reconcile(ctx) + + // Start background polling + go r.run(ctx) + + return r, nil +} + +// Stop stops the background reconciler +func (r *NamespaceReconciler) Stop() { + if r.cancel != nil { + r.cancel() + } +} + +// run polls periodically to reconcile namespaces +func (r *NamespaceReconciler) run(ctx context.Context) { + ticker := time.NewTicker(reconcileInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + r.reconcile(ctx) + } + } +} + +// reconcile ensures all watched namespaces have the required resources +func (r *NamespaceReconciler) reconcile(ctx context.Context) { + namespaces := r.watchedNamespaces + + // If watching all namespaces, list them + if r.watchesAllNamespaces() { + nsList := &corev1.NamespaceList{} + if err := r.kcli.List(ctx, nsList); err != nil { + r.logger.WithError(err).Warn("failed to list namespaces") + return + } + namespaces = make([]string, 0, len(nsList.Items)) + for _, ns := range nsList.Items { + namespaces = append(namespaces, ns.Name) + } + } + + for _, ns := range namespaces { + if err := r.reconcileNamespace(ctx, ns); err != nil { + r.logger.WithError(err).Warnf("failed to reconcile namespace %s", ns) + } + } +} + +// watchesAllNamespaces returns true if "*" is in the watched namespaces list +func (r *NamespaceReconciler) watchesAllNamespaces() bool { + return slices.Contains(r.watchedNamespaces, "*") +} + +// reconcileNamespace creates namespace if needed and ensures required resources exist +func (r *NamespaceReconciler) reconcileNamespace(ctx context.Context, namespace string) error { + // Create namespace if it doesn't exist + ns := &corev1.Namespace{} + err := r.kcli.Get(ctx, client.ObjectKey{Name: namespace}, ns) + if k8serrors.IsNotFound(err) { + ns = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: namespace}, + } + if err := r.kcli.Create(ctx, ns); err != nil && !k8serrors.IsAlreadyExists(err) { + return fmt.Errorf("create namespace: %w", err) + } + r.logger.Infof("created namespace %s", namespace) + } else if err != nil { + return fmt.Errorf("get namespace: %w", err) + } + + if err := r.ensureImagePullSecret(ctx, namespace); err != nil { + return fmt.Errorf("ensure image pull secret: %w", err) + } + + if err := r.ensureCAConfigmap(ctx, namespace); err != nil { + return fmt.Errorf("ensure ca configmap: %w", err) + } + + return nil +} + +// ensureImagePullSecret creates or updates the image pull secret in a namespace +func (r *NamespaceReconciler) ensureImagePullSecret(ctx context.Context, namespace string) error { + // Skip if no registry settings + if r.registrySettings == nil || r.registrySettings.ImagePullSecretName == "" || r.registrySettings.ImagePullSecretValue == "" { + return nil + } + + secretData, err := base64.StdEncoding.DecodeString(r.registrySettings.ImagePullSecretValue) + if err != nil { + return fmt.Errorf("decode secret value: %w", err) + } + + secret := &corev1.Secret{} + key := client.ObjectKey{Namespace: namespace, Name: r.registrySettings.ImagePullSecretName} + err = r.kcli.Get(ctx, key, secret) + + if k8serrors.IsNotFound(err) { + secret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: r.registrySettings.ImagePullSecretName, + Namespace: namespace, + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + ".dockerconfigjson": secretData, + }, + } + if err := r.kcli.Create(ctx, secret); err != nil { + return fmt.Errorf("create secret: %w", err) + } + r.logger.Infof("created image pull secret %s in namespace %s", r.registrySettings.ImagePullSecretName, namespace) + return nil + } + if err != nil { + return fmt.Errorf("get secret: %w", err) + } + + // Update existing secret if data differs + if string(secret.Data[".dockerconfigjson"]) != string(secretData) { + secret.Data[".dockerconfigjson"] = secretData + if err := r.kcli.Update(ctx, secret); err != nil { + return fmt.Errorf("update secret: %w", err) + } + r.logger.Infof("updated image pull secret %s in namespace %s", r.registrySettings.ImagePullSecretName, namespace) + } + + return nil +} + +// ensureCAConfigmap ensures the CA configmap exists in the namespace +func (r *NamespaceReconciler) ensureCAConfigmap(ctx context.Context, namespace string) error { + // Skip if no CA bundle path + if r.hostCABundlePath == "" { + return nil + } + + logFn := func(format string, args ...interface{}) { + r.logger.Infof(format, args...) + } + + return adminconsole.EnsureCAConfigmap(ctx, addonstypes.LogFunc(logFn), r.kcli, r.mcli, namespace, r.hostCABundlePath) +} diff --git a/api/internal/managers/app/install/namespaces_reconciler_test.go b/api/internal/managers/app/install/namespaces_reconciler_test.go new file mode 100644 index 0000000000..0ec08e3a19 --- /dev/null +++ b/api/internal/managers/app/install/namespaces_reconciler_test.go @@ -0,0 +1,447 @@ +package install + +import ( + "encoding/base64" + "os" + "testing" + "time" + + "github.com/replicatedhq/embedded-cluster/api/pkg/logger" + "github.com/replicatedhq/embedded-cluster/api/types" + "github.com/replicatedhq/embedded-cluster/pkg/release" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + metadatafake "k8s.io/client-go/metadata/fake" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestRunNamespaceReconciler(t *testing.T) { + dockerConfigJSON := `{"auths":{"registry.example.com":{"auth":"dXNlcjpwYXNz"}}}` + + appSlug := "test-app" + + tests := []struct { + name string + applicationYAML string + registrySettings *types.RegistrySettings + withCABundle bool + existingNamespaces []string + existingSecrets []corev1.Secret + existingConfigMaps []corev1.ConfigMap + + wantWatchedNs []string + wantCreatedNs []string + wantSecretInNs []string + wantNoSecretInNs []string + wantCAConfigmapInNs []string + wantNoCAConfigmapInNs []string + wantErr bool + }{ + { + name: "no application - only app namespace", + applicationYAML: "", + registrySettings: &types.RegistrySettings{ + ImagePullSecretName: "test-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + }, + existingNamespaces: []string{appSlug}, + wantWatchedNs: []string{appSlug}, + wantCreatedNs: []string{}, + wantSecretInNs: []string{appSlug}, + wantNoCAConfigmapInNs: []string{appSlug}, + }, + { + name: "application with no additional namespaces", + applicationYAML: `apiVersion: kots.io/v1beta1 +kind: Application +metadata: + name: test-app +spec: + title: Test App`, + registrySettings: &types.RegistrySettings{ + ImagePullSecretName: "test-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + }, + existingNamespaces: []string{appSlug}, + wantWatchedNs: []string{appSlug}, + wantCreatedNs: []string{}, + wantSecretInNs: []string{appSlug}, + wantNoCAConfigmapInNs: []string{appSlug}, + }, + { + name: "application with additional namespaces", + applicationYAML: `apiVersion: kots.io/v1beta1 +kind: Application +metadata: + name: test-app +spec: + title: Test App + additionalNamespaces: + - app-ns-1 + - app-ns-2`, + registrySettings: &types.RegistrySettings{ + ImagePullSecretName: "test-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + }, + existingNamespaces: []string{appSlug}, + wantWatchedNs: []string{appSlug, "app-ns-1", "app-ns-2"}, + wantCreatedNs: []string{"app-ns-1", "app-ns-2"}, + wantSecretInNs: []string{appSlug, "app-ns-1", "app-ns-2"}, + wantNoCAConfigmapInNs: []string{appSlug, "app-ns-1", "app-ns-2"}, + }, + { + name: "application with wildcard namespace", + applicationYAML: `apiVersion: kots.io/v1beta1 +kind: Application +metadata: + name: test-app +spec: + title: Test App + additionalNamespaces: + - "*"`, + registrySettings: &types.RegistrySettings{ + ImagePullSecretName: "test-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + }, + existingNamespaces: []string{appSlug, "existing-ns-1", "existing-ns-2"}, + wantWatchedNs: []string{appSlug, "*"}, + wantCreatedNs: []string{}, + wantSecretInNs: []string{appSlug, "existing-ns-1", "existing-ns-2"}, + wantNoCAConfigmapInNs: []string{appSlug, "existing-ns-1", "existing-ns-2"}, + }, + { + name: "no registry settings - no secrets created", + applicationYAML: `apiVersion: kots.io/v1beta1 +kind: Application +metadata: + name: test-app +spec: + title: Test App + additionalNamespaces: + - app-ns`, + registrySettings: nil, + existingNamespaces: []string{appSlug}, + wantWatchedNs: []string{appSlug, "app-ns"}, + wantCreatedNs: []string{"app-ns"}, + wantSecretInNs: []string{}, + wantNoSecretInNs: []string{appSlug, "app-ns"}, + wantNoCAConfigmapInNs: []string{appSlug, "app-ns"}, + }, + { + name: "with CA bundle path - configmaps created", + applicationYAML: `apiVersion: kots.io/v1beta1 +kind: Application +metadata: + name: test-app +spec: + title: Test App + additionalNamespaces: + - app-ns`, + registrySettings: &types.RegistrySettings{ + ImagePullSecretName: "test-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + }, + withCABundle: true, + existingNamespaces: []string{appSlug}, + wantWatchedNs: []string{appSlug, "app-ns"}, + wantCreatedNs: []string{"app-ns"}, + wantSecretInNs: []string{appSlug, "app-ns"}, + wantCAConfigmapInNs: []string{appSlug, "app-ns"}, + }, + { + name: "updates existing secret with different data", + applicationYAML: `apiVersion: kots.io/v1beta1 +kind: Application +metadata: + name: test-app +spec: + title: Test App`, + registrySettings: &types.RegistrySettings{ + ImagePullSecretName: "test-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + }, + existingNamespaces: []string{appSlug}, + existingSecrets: []corev1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "test-secret", + Namespace: appSlug, + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + ".dockerconfigjson": []byte(`{"auths":{"old.registry.com":{}}}`), + }, + }, + }, + wantWatchedNs: []string{appSlug}, + wantCreatedNs: []string{}, + wantSecretInNs: []string{appSlug}, + }, + { + name: "updates existing CA configmap with different data", + applicationYAML: `apiVersion: kots.io/v1beta1 +kind: Application +metadata: + name: test-app +spec: + title: Test App`, + registrySettings: &types.RegistrySettings{ + ImagePullSecretName: "test-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + }, + withCABundle: true, + existingNamespaces: []string{appSlug}, + existingConfigMaps: []corev1.ConfigMap{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "kotsadm-private-cas", + Namespace: appSlug, + Annotations: map[string]string{ + "replicated.com/cas-checksum": "old-checksum", + }, + }, + Data: map[string]string{ + "ca_0.crt": "old-ca-content", + }, + }, + }, + wantWatchedNs: []string{appSlug}, + wantCreatedNs: []string{}, + wantSecretInNs: []string{appSlug}, + wantCAConfigmapInNs: []string{appSlug}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Setenv("ENABLE_V3", "1") + + // Set up release data + releaseData := map[string][]byte{ + "channelrelease.yaml": []byte("# channel release object\nappSlug: test-app"), + } + if tt.applicationYAML != "" { + releaseData["application.yaml"] = []byte(tt.applicationYAML) + } + err := release.SetReleaseDataForTests(releaseData) + require.NoError(t, err) + + // Build fake client with existing namespaces, secrets, and configmaps + builder := fake.NewClientBuilder().WithScheme(scheme.Scheme) + for _, nsName := range tt.existingNamespaces { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: nsName}, + } + builder = builder.WithObjects(ns) + } + for i := range tt.existingSecrets { + builder = builder.WithObjects(&tt.existingSecrets[i]) + } + for i := range tt.existingConfigMaps { + builder = builder.WithObjects(&tt.existingConfigMaps[i]) + } + fakeKcli := builder.Build() + + // Create fake metadata client + fakeMcli := metadatafake.NewSimpleMetadataClient(metadatafake.NewTestScheme()) + + // Handle temp CA file + var hostCABundlePath string + if tt.withCABundle { + tmpFile, err := os.CreateTemp("", "ca-bundle-*.crt") + require.NoError(t, err) + defer os.Remove(tmpFile.Name()) + _, err = tmpFile.WriteString("-----BEGIN CERTIFICATE-----\ntest-ca-content\n-----END CERTIFICATE-----") + require.NoError(t, err) + tmpFile.Close() + hostCABundlePath = tmpFile.Name() + } + + // Run the reconciler + reconciler, err := runNamespaceReconciler( + t.Context(), + fakeKcli, + fakeMcli, + tt.registrySettings, + hostCABundlePath, + logger.NewDiscardLogger(), + ) + + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NotNil(t, reconciler) + defer reconciler.Stop() + + // Verify watched namespaces + assert.Equal(t, tt.wantWatchedNs, reconciler.watchedNamespaces) + + // Verify namespaces were created + for _, nsName := range tt.wantCreatedNs { + ns := &corev1.Namespace{} + err := fakeKcli.Get(t.Context(), client.ObjectKey{Name: nsName}, ns) + require.NoError(t, err, "namespace %s should be created", nsName) + } + + // Verify secrets were created in expected namespaces + for _, nsName := range tt.wantSecretInNs { + secret := &corev1.Secret{} + err := fakeKcli.Get(t.Context(), client.ObjectKey{ + Namespace: nsName, + Name: tt.registrySettings.ImagePullSecretName, + }, secret) + require.NoError(t, err, "secret should exist in namespace %s", nsName) + assert.Equal(t, corev1.SecretTypeDockerConfigJson, secret.Type) + assert.Equal(t, dockerConfigJSON, string(secret.Data[".dockerconfigjson"])) + } + + // Verify CA configmaps were created in expected namespaces + for _, nsName := range tt.wantCAConfigmapInNs { + configMap := &corev1.ConfigMap{} + err := fakeKcli.Get(t.Context(), client.ObjectKey{ + Namespace: nsName, + Name: "kotsadm-private-cas", + }, configMap) + require.NoError(t, err, "CA configmap should exist in namespace %s", nsName) + assert.Contains(t, configMap.Data["ca_0.crt"], "test-ca-content") + } + + // Verify secrets were NOT created in namespaces where they shouldn't be + for _, nsName := range tt.wantNoSecretInNs { + secret := &corev1.Secret{} + err := fakeKcli.Get(t.Context(), client.ObjectKey{ + Namespace: nsName, + Name: "test-secret", + }, secret) + assert.Error(t, err, "secret should not exist in namespace %s", nsName) + } + + // Verify CA configmaps were NOT created in namespaces where they shouldn't be + for _, nsName := range tt.wantNoCAConfigmapInNs { + configMap := &corev1.ConfigMap{} + err := fakeKcli.Get(t.Context(), client.ObjectKey{ + Namespace: nsName, + Name: "kotsadm-private-cas", + }, configMap) + assert.Error(t, err, "CA configmap should not exist in namespace %s", nsName) + } + }) + } +} + +func TestRunNamespaceReconciler_DynamicNamespace(t *testing.T) { + t.Setenv("ENABLE_V3", "1") + + dockerConfigJSON := `{"auths":{"registry.example.com":{"auth":"dXNlcjpwYXNz"}}}` + appSlug := "test-app" + + // Set up release data with wildcard namespace + releaseData := map[string][]byte{ + "channelrelease.yaml": []byte("# channel release object\nappSlug: " + appSlug), + "application.yaml": []byte(`apiVersion: kots.io/v1beta1 +kind: Application +metadata: + name: test-app +spec: + title: Test App + additionalNamespaces: + - "*"`), + } + require.NoError(t, release.SetReleaseDataForTests(releaseData)) + + // Start with only the app namespace + appNs := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: appSlug}, + } + fakeKcli := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(appNs).Build() + fakeMcli := metadatafake.NewSimpleMetadataClient(metadatafake.NewTestScheme()) + + registrySettings := &types.RegistrySettings{ + ImagePullSecretName: "test-secret", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), + } + + // Run the reconciler + reconciler, err := runNamespaceReconciler( + t.Context(), + fakeKcli, + fakeMcli, + registrySettings, + "", + logger.NewDiscardLogger(), + ) + require.NoError(t, err) + defer reconciler.Stop() + + // Verify initial state - secret exists in app namespace + secret := &corev1.Secret{} + err = fakeKcli.Get(t.Context(), client.ObjectKey{ + Namespace: appSlug, + Name: "test-secret", + }, secret) + require.NoError(t, err, "secret should exist in app namespace") + + // Create a new namespace dynamically (simulating external namespace creation) + newNs := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: "dynamic-ns"}, + } + require.NoError(t, fakeKcli.Create(t.Context(), newNs)) + + // Wait for the background reconciler to create the secret in the new namespace + assert.Eventually(t, func() bool { + err := fakeKcli.Get(t.Context(), client.ObjectKey{ + Namespace: "dynamic-ns", + Name: "test-secret", + }, secret) + return err == nil + }, 10*time.Second, 100*time.Millisecond, "secret should be created in dynamic namespace by background reconciler") + + assert.Equal(t, corev1.SecretTypeDockerConfigJson, secret.Type) + assert.Equal(t, dockerConfigJSON, string(secret.Data[".dockerconfigjson"])) +} + +func TestNamespaceReconciler_watchesAllNamespaces(t *testing.T) { + tests := []struct { + name string + watchedNamespaces []string + want bool + }{ + { + name: "returns true when * is in list", + watchedNamespaces: []string{"kotsadm", "*"}, + want: true, + }, + { + name: "returns true when only * is in list", + watchedNamespaces: []string{"*"}, + want: true, + }, + { + name: "returns false when * is not in list", + watchedNamespaces: []string{"kotsadm", "app-ns"}, + want: false, + }, + { + name: "returns false for empty list", + watchedNamespaces: []string{}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &NamespaceReconciler{ + watchedNamespaces: tt.watchedNamespaces, + } + + got := r.watchesAllNamespaces() + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/api/internal/managers/app/install/status.go b/api/internal/managers/app/install/status.go index 192244feaa..d9e5cb62aa 100644 --- a/api/internal/managers/app/install/status.go +++ b/api/internal/managers/app/install/status.go @@ -1,12 +1,19 @@ package install import ( - "fmt" + "time" + + "github.com/replicatedhq/embedded-cluster/api/types" ) -func (m *appInstallManager) addLogs(format string, v ...any) { - msg := fmt.Sprintf(format, v...) - if err := m.appInstallStore.AddLogs(msg); err != nil { - m.logger.WithError(err).Error("add log") - } +func (m *appInstallManager) GetStatus() (types.AppInstall, error) { + return m.appInstallStore.Get() +} + +func (m *appInstallManager) setComponentStatus(componentName string, state types.State, description string) error { + return m.appInstallStore.SetComponentStatus(componentName, types.Status{ + State: state, + Description: description, + LastUpdated: time.Now(), + }) } diff --git a/api/internal/managers/app/install/util.go b/api/internal/managers/app/install/util.go index 086e49d90a..e725ec76da 100644 --- a/api/internal/managers/app/install/util.go +++ b/api/internal/managers/app/install/util.go @@ -2,38 +2,49 @@ package install import ( "fmt" - "io" - "strings" + "os" "github.com/replicatedhq/embedded-cluster/api/internal/clients" "k8s.io/cli-runtime/pkg/genericclioptions" ) -// logWriter is an io.Writer that captures output and feeds it to the logs -type logWriter struct { - manager *appInstallManager +func (m *appInstallManager) logFn(component string) func(format string, v ...interface{}) { + return func(format string, v ...interface{}) { + m.logger.WithField("component", component).Debugf(format, v...) + m.addLogs(component, format, v...) + } } -func (m *appInstallManager) newLogWriter() io.Writer { - return &logWriter{manager: m} +func (m *appInstallManager) addLogs(component string, format string, v ...interface{}) { + msg := fmt.Sprintf("[%s] %s", component, fmt.Sprintf(format, v...)) + if err := m.appInstallStore.AddLogs(msg); err != nil { + m.logger.WithError(err).Error("add log") + } } -func (lw *logWriter) Write(p []byte) (n int, err error) { - output := strings.TrimSpace(string(p)) - if output != "" { - lw.manager.addLogs("[kots] %s", output) - lw.manager.logger.WithField("component", "kots").Debug(output) +func (m *appInstallManager) writeChartArchiveToTemp(chartArchive []byte) (string, error) { + tmpFile, err := os.CreateTemp("", "helm-chart-*.tgz") + if err != nil { + return "", fmt.Errorf("create temp file: %w", err) + } + defer tmpFile.Close() + + if _, err := tmpFile.Write(chartArchive); err != nil { + _ = os.Remove(tmpFile.Name()) + return "", fmt.Errorf("write chart archive: %w", err) } - return len(p), nil + + return tmpFile.Name(), nil } -func (m *appInstallManager) initKubeClient() error { - if m.kcli == nil { - var restClientGetter genericclioptions.RESTClientGetter - if m.kubernetesEnvSettings != nil { - restClientGetter = m.kubernetesEnvSettings.RESTClientGetter() - } +// setupClients initializes the kube, metadata, and helm clients if they are not already set. +func (m *appInstallManager) setupClients() error { + var restClientGetter genericclioptions.RESTClientGetter + if m.kubernetesEnvSettings != nil { + restClientGetter = m.kubernetesEnvSettings.RESTClientGetter() + } + if m.kcli == nil { kcli, err := clients.NewKubeClient(clients.KubeClientOptions{RESTClientGetter: restClientGetter}) if err != nil { return fmt.Errorf("create kube client: %w", err) @@ -41,5 +52,17 @@ func (m *appInstallManager) initKubeClient() error { m.kcli = kcli } + if m.mcli == nil && restClientGetter != nil { + mcli, err := clients.NewMetadataClient(clients.KubeClientOptions{RESTClientGetter: restClientGetter}) + if err != nil { + return fmt.Errorf("create metadata client: %w", err) + } + m.mcli = mcli + } + + if m.hcli == nil { + return fmt.Errorf("helm client is required") + } + return nil } diff --git a/api/internal/managers/app/install/util_test.go b/api/internal/managers/app/install/util_test.go index 7410244db0..a07e084120 100644 --- a/api/internal/managers/app/install/util_test.go +++ b/api/internal/managers/app/install/util_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestLogWriter_Write(t *testing.T) { +func TestLogFn_Write(t *testing.T) { // Create store store := appinstallstore.NewMemoryStore() @@ -29,31 +29,31 @@ func TestLogWriter_Write(t *testing.T) { { name: "Single line output", input: "Installing package X", - expectedOutput: "[kots] Installing package X", + expectedOutput: "[app] Installing package X", expectedInLogs: true, }, { name: "Output with newline", input: "Installing package Y\n", - expectedOutput: "[kots] Installing package Y", + expectedOutput: "[app] Installing package Y", expectedInLogs: true, }, { name: "Empty string", input: "", - expectedOutput: "", - expectedInLogs: false, + expectedOutput: "[app]", + expectedInLogs: true, }, { name: "Whitespace only", input: " \n\t ", - expectedOutput: "", - expectedInLogs: false, + expectedOutput: "[app]", + expectedInLogs: true, }, { name: "Multiple lines", input: "Line 1\nLine 2\n", - expectedOutput: "[kots] Line 1\nLine 2", + expectedOutput: "[app] Line 1\nLine 2", expectedInLogs: true, }, } @@ -65,12 +65,10 @@ func TestLogWriter_Write(t *testing.T) { concreteManager.appInstallStore = newStore // Create new writer for the test - testWriter := concreteManager.newLogWriter() + logFn := concreteManager.logFn("app") // Write to log writer - n, err := testWriter.Write([]byte(tt.input)) - assert.NoError(t, err) - assert.Equal(t, len(tt.input), n) + logFn(tt.input) // Check if logs were added logs, err := concreteManager.appInstallStore.GetLogs() @@ -85,15 +83,15 @@ func TestLogWriter_Write(t *testing.T) { } } -func TestLogWriter_WriteMultipleOperations(t *testing.T) { +func TestLogFn_MultipleOperations(t *testing.T) { // Create concrete manager directly for testing utilities concreteManager := &appInstallManager{ appInstallStore: appinstallstore.NewMemoryStore(), logger: logger.NewDiscardLogger(), } - // Create log writer - writer := concreteManager.newLogWriter() + // Create log function + logFn := concreteManager.logFn("app") // Write multiple entries entries := []string{ @@ -104,9 +102,7 @@ func TestLogWriter_WriteMultipleOperations(t *testing.T) { } for _, entry := range entries { - n, err := writer.Write([]byte(entry)) - assert.NoError(t, err) - assert.Equal(t, len(entry), n) + logFn(entry) } // Verify all entries are in logs @@ -114,7 +110,7 @@ func TestLogWriter_WriteMultipleOperations(t *testing.T) { require.NoError(t, err) for _, entry := range entries { - expected := "[kots] " + entry + expected := "[app] " + entry assert.Contains(t, logs, expected) } @@ -122,12 +118,12 @@ func TestLogWriter_WriteMultipleOperations(t *testing.T) { lines := strings.Split(strings.TrimSpace(logs), "\n") assert.Len(t, lines, len(entries)) for i, entry := range entries { - expected := "[kots] " + entry + expected := "[app] " + entry assert.Equal(t, expected, lines[i]) } } -func TestLogWriter_LargeOutput(t *testing.T) { +func TestLogFn_LargeOutput(t *testing.T) { // Create concrete manager directly for testing utilities concreteManager := &appInstallManager{ appInstallStore: appinstallstore.NewMemoryStore(), @@ -135,24 +131,22 @@ func TestLogWriter_LargeOutput(t *testing.T) { } // Create log writer - writer := concreteManager.newLogWriter() + logFn := concreteManager.logFn("app") // Create a large output string largeOutput := strings.Repeat("A", 1000) // Write large output - n, err := writer.Write([]byte(largeOutput)) - assert.NoError(t, err) - assert.Equal(t, 1000, n) + logFn(largeOutput) // Verify it was logged with prefix logs, err := concreteManager.appInstallStore.GetLogs() require.NoError(t, err) - expected := "[kots] " + largeOutput + expected := "[app] " + largeOutput assert.Contains(t, logs, expected) } -func TestLogWriter_BinaryData(t *testing.T) { +func TestLogFn_BinaryData(t *testing.T) { // Create concrete manager directly for testing utilities concreteManager := &appInstallManager{ appInstallStore: appinstallstore.NewMemoryStore(), @@ -160,17 +154,15 @@ func TestLogWriter_BinaryData(t *testing.T) { } // Create log writer - writer := concreteManager.newLogWriter() + logFn := concreteManager.logFn("app") // Write binary data (should still work as io.Writer) binaryData := []byte{0x00, 0x01, 0x02, 0xFF, 0xFE} - n, err := writer.Write(binaryData) - assert.NoError(t, err) - assert.Equal(t, len(binaryData), n) + logFn(string(binaryData)) // Verify it was processed (though it may not be readable text) logs, err := concreteManager.appInstallStore.GetLogs() require.NoError(t, err) - // Should contain the [kots] prefix at minimum since binary data gets converted to string - assert.Contains(t, logs, "[kots]") + // Should contain the [app] prefix at minimum since binary data gets converted to string + assert.Contains(t, logs, "[app]") } diff --git a/api/internal/managers/app/release/manager.go b/api/internal/managers/app/release/manager.go index 8622f862ac..4b00010c26 100644 --- a/api/internal/managers/app/release/manager.go +++ b/api/internal/managers/app/release/manager.go @@ -19,6 +19,7 @@ import ( // AppReleaseManager provides methods for managing the release of an app type AppReleaseManager interface { ExtractAppPreflightSpec(ctx context.Context, configValues types.AppConfigValues, proxySpec *ecv1beta1.ProxySpec, registrySettings *types.RegistrySettings) (*troubleshootv1beta2.PreflightSpec, error) + ExtractInstallableHelmCharts(ctx context.Context, configValues types.AppConfigValues, proxySpec *ecv1beta1.ProxySpec, registrySettings *types.RegistrySettings) ([]types.InstallableHelmChart, error) } type appReleaseManager struct { diff --git a/api/internal/managers/app/release/manager_mock.go b/api/internal/managers/app/release/manager_mock.go index 4bdeb2652e..941e72ac2f 100644 --- a/api/internal/managers/app/release/manager_mock.go +++ b/api/internal/managers/app/release/manager_mock.go @@ -24,3 +24,12 @@ func (m *MockAppReleaseManager) ExtractAppPreflightSpec(ctx context.Context, con } return args.Get(0).(*troubleshootv1beta2.PreflightSpec), args.Error(1) } + +// ExtractInstallableHelmCharts mocks the ExtractInstallableHelmCharts method +func (m *MockAppReleaseManager) ExtractInstallableHelmCharts(ctx context.Context, configValues types.AppConfigValues, proxySpec *ecv1beta1.ProxySpec, registrySettings *types.RegistrySettings) ([]types.InstallableHelmChart, error) { + args := m.Called(ctx, configValues, proxySpec, registrySettings) + if args.Get(0) == nil { + return nil, args.Error(1) + } + return args.Get(0).([]types.InstallableHelmChart), args.Error(1) +} diff --git a/api/internal/managers/app/release/template.go b/api/internal/managers/app/release/template.go index 6344de6c98..d0c6a810bd 100644 --- a/api/internal/managers/app/release/template.go +++ b/api/internal/managers/app/release/template.go @@ -5,6 +5,7 @@ import ( "fmt" "maps" "os" + "sort" "strconv" "github.com/replicatedhq/embedded-cluster/api/pkg/template" @@ -56,6 +57,59 @@ func (m *appReleaseManager) ExtractAppPreflightSpec(ctx context.Context, configV return mergedSpec, nil } +// ExtractInstallableHelmCharts extracts and processes installable Helm charts from app releases +func (m *appReleaseManager) ExtractInstallableHelmCharts(ctx context.Context, configValues types.AppConfigValues, proxySpec *ecv1beta1.ProxySpec, registrySettings *types.RegistrySettings) ([]types.InstallableHelmChart, error) { + // Template Helm chart CRs with config values + templatedCRs, err := m.templateHelmChartCRs(configValues, proxySpec, registrySettings) + if err != nil { + return nil, fmt.Errorf("template helm chart CRs: %w", err) + } + + var installableCharts []types.InstallableHelmChart + + // Iterate over each templated CR and create installable chart with processed values + for _, cr := range templatedCRs { + // Check if the chart should be excluded + if !cr.Spec.Exclude.IsEmpty() { + exclude, err := cr.Spec.Exclude.Boolean() + if err != nil { + return nil, fmt.Errorf("parse templated CR exclude for %s: %w", cr.Name, err) + } + if exclude { + continue + } + } + + // Find the corresponding chart archive for this HelmChart CR + chartArchive, err := findChartArchive(m.releaseData.HelmChartArchives, cr) + if err != nil { + return nil, fmt.Errorf("find chart archive for %s: %w", cr.Name, err) + } + + // Generate Helm values from the templated CR + values, err := generateHelmValues(cr) + if err != nil { + return nil, fmt.Errorf("generate helm values for chart %s: %w", cr.Name, err) + } + + // Create installable chart with archive, processed values, and CR + installableChart := types.InstallableHelmChart{ + Archive: chartArchive, + Values: values, + CR: cr, + } + + installableCharts = append(installableCharts, installableChart) + } + + // Sort charts by weight field before returning + sort.Slice(installableCharts, func(i, j int) bool { + return installableCharts[i].CR.GetWeight() < installableCharts[j].CR.GetWeight() + }) + + return installableCharts, nil +} + // templateHelmChartCRs templates the HelmChart CRs from release data using the template engine and config values func (m *appReleaseManager) templateHelmChartCRs(configValues types.AppConfigValues, proxySpec *ecv1beta1.ProxySpec, registrySettings *types.RegistrySettings) ([]*kotsv1beta2.HelmChart, error) { if m.templateEngine == nil { @@ -172,7 +226,7 @@ func generateHelmValues(templatedCR *kotsv1beta2.HelmChart) (map[string]any, err } // Start with the base values - mergedValues := templatedCR.Spec.Values + mergedValues := maps.Clone(templatedCR.Spec.Values) if mergedValues == nil { mergedValues = map[string]kotsv1beta2.MappedChartValue{} } diff --git a/api/internal/managers/app/release/template_test.go b/api/internal/managers/app/release/template_test.go index 8deda941b4..0a33dc1a3d 100644 --- a/api/internal/managers/app/release/template_test.go +++ b/api/internal/managers/app/release/template_test.go @@ -746,12 +746,12 @@ spec: configValues: types.AppConfigValues{}, proxySpec: &ecv1beta1.ProxySpec{}, registrySettings: &types.RegistrySettings{ - HasLocalRegistry: true, - Host: "10.128.0.11:5000", - Address: "10.128.0.11:5000/myapp", - Namespace: "myapp", - ImagePullSecretName: "embedded-cluster-registry", - ImagePullSecretValue: "dGVzdC1zZWNyZXQtdmFsdWU=", + HasLocalRegistry: true, + LocalRegistryHost: "10.128.0.11:5000", + LocalRegistryAddress: "10.128.0.11:5000/myapp", + LocalRegistryNamespace: "myapp", + ImagePullSecretName: "test-app-registry", + ImagePullSecretValue: "dGVzdC1zZWNyZXQtdmFsdWU=", }, expected: []*kotsv1beta2.HelmChart{ createHelmChartCRFromYAML(` @@ -768,7 +768,7 @@ spec: image: repository: "10.128.0.11:5000/myapp/nginx" imagePullSecrets: - - name: "embedded-cluster-registry" + - name: "test-app-registry" registry: host: "10.128.0.11:5000" address: "10.128.0.11:5000/myapp" @@ -1974,6 +1974,738 @@ data: } } +func TestAppReleaseManager_ExtractInstallableHelmCharts(t *testing.T) { + tests := []struct { + name string + helmChartCRs [][]byte + chartArchives [][]byte + configValues types.AppConfigValues + proxySpec *ecv1beta1.ProxySpec + registrySettings *types.RegistrySettings + expectError bool + errorContains string + expected []types.InstallableHelmChart + }{ + { + name: "no helm charts returns empty slice", + helmChartCRs: [][]byte{}, + chartArchives: [][]byte{}, + configValues: types.AppConfigValues{}, + expectError: false, + expected: nil, + }, + { + name: "single chart with basic configuration", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: nginx-chart +spec: + namespace: repl{{ConfigOption "namespace"}} + releaseName: repl{{ConfigOption "release_name"}} + chart: + name: nginx + chartVersion: "1.0.0" + values: + replicaCount: "3" + image: + repository: nginx + tag: '{{repl ConfigOption "image_tag"}}' + service: + type: ClusterIP + port: 80 + optionalValues: + - when: '{{repl ConfigOptionEquals "enable_ingress" "true"}}' + values: + ingress: + enabled: true + host: '{{repl ConfigOption "ingress_host"}}'`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + }, + configValues: types.AppConfigValues{ + "namespace": {Value: "custom-namespace"}, + "release_name": {Value: "custom-release-name"}, + "image_tag": {Value: "1.20.0"}, + "enable_ingress": {Value: "true"}, + "ingress_host": {Value: "nginx.example.com"}, + }, + expectError: false, + expected: []types.InstallableHelmChart{ + { + Archive: createTestChartArchive(t, "nginx", "1.0.0"), + Values: map[string]any{ + "replicaCount": "3", + "image": map[string]any{ + "repository": "nginx", + "tag": "1.20.0", + }, + "service": map[string]any{ + "type": "ClusterIP", + "port": float64(80), + }, + "ingress": map[string]any{ + "enabled": true, + "host": "nginx.example.com", + }, + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: nginx-chart +spec: + namespace: custom-namespace + releaseName: custom-release-name + chart: + name: nginx + chartVersion: "1.0.0" + values: + replicaCount: "3" + image: + repository: nginx + tag: "1.20.0" + service: + type: ClusterIP + port: 80 + optionalValues: + - when: "true" + values: + ingress: + enabled: true + host: "nginx.example.com"`), + }, + }, + }, + { + name: "chart with exclude=true should be skipped", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: excluded-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + exclude: '{{repl ConfigOptionEquals "skip_nginx" "true"}}' + values: + replicaCount: "2"`), + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: included-chart +spec: + chart: + name: redis + chartVersion: "2.0.0" + exclude: false + values: + persistence: + enabled: true`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + createTestChartArchive(t, "redis", "2.0.0"), + }, + configValues: types.AppConfigValues{ + "skip_nginx": {Value: "true"}, + }, + expectError: false, + expected: []types.InstallableHelmChart{ + { + Archive: createTestChartArchive(t, "redis", "2.0.0"), + Values: map[string]any{ + "persistence": map[string]any{ + "enabled": true, + }, + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: included-chart +spec: + chart: + name: redis + chartVersion: "2.0.0" + exclude: false + values: + persistence: + enabled: true`), + }, + }, + }, + { + name: "chart with recursive merge optional values", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: merge-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + service: + type: '{{repl ConfigOption "service_type"}}' + port: 80 + replicaCount: "1" + optionalValues: + - when: '{{repl ConfigOption "enable_ssl"}}' + recursiveMerge: true + values: + service: + type: LoadBalancer + ssl: + enabled: true`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + }, + configValues: types.AppConfigValues{ + "service_type": {Value: "ClusterIP"}, + "enable_ssl": {Value: "true"}, + }, + expectError: false, + expected: []types.InstallableHelmChart{ + { + Archive: createTestChartArchive(t, "nginx", "1.0.0"), + Values: map[string]any{ + "replicaCount": "1", + "service": map[string]any{ + "type": "LoadBalancer", // from optional values (overrode base value) + "port": float64(80), // from base values (preserved) + }, + "ssl": map[string]any{ + "enabled": true, // from optional values (added) + }, + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: merge-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + service: + type: "ClusterIP" + port: 80 + replicaCount: "1" + optionalValues: + - when: "true" + recursiveMerge: true + values: + service: + type: LoadBalancer + ssl: + enabled: true`), + }, + }, + }, + { + name: "chart with direct replacement optional values", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: replace-chart +spec: + chart: + name: redis + chartVersion: "2.0.0" + values: + persistence: + enabled: '{{repl ConfigOption "enable_persistence"}}' + size: "5Gi" + optionalValues: + - when: '{{repl ConfigOption "redis_persistence"}}' + recursiveMerge: false + values: + persistence: + size: "20Gi"`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "redis", "2.0.0"), + }, + configValues: types.AppConfigValues{ + "enable_persistence": {Value: "true"}, + "redis_persistence": {Value: "true"}, + }, + expectError: false, + expected: []types.InstallableHelmChart{ + { + Archive: createTestChartArchive(t, "redis", "2.0.0"), + Values: map[string]any{ + "persistence": map[string]any{ + "size": "20Gi", // from optional values (direct replacement) + // Note: enabled=true is GONE because entire persistence key was replaced + }, + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: replace-chart +spec: + chart: + name: redis + chartVersion: "2.0.0" + values: + persistence: + enabled: "true" + size: "5Gi" + optionalValues: + - when: "true" + recursiveMerge: false + values: + persistence: + size: "20Gi"`), + }, + }, + }, + { + name: "chart with proxy template functions", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: proxy-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + proxy: + http: '{{repl HTTPProxy}}' + https: '{{repl HTTPSProxy}}' + noProxy: '{{repl NoProxy | join ","}}' + optionalValues: + - when: '{{repl if HTTPProxy}}true{{repl else}}false{{repl end}}' + values: + proxyEnabled: true`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + }, + configValues: types.AppConfigValues{}, + proxySpec: &ecv1beta1.ProxySpec{ + HTTPProxy: "http://proxy.example.com:8080", + HTTPSProxy: "https://proxy.example.com:8443", + NoProxy: "localhost,127.0.0.1,.cluster.local", + }, + expectError: false, + expected: []types.InstallableHelmChart{ + { + Archive: createTestChartArchive(t, "nginx", "1.0.0"), + Values: map[string]any{ + "proxy": map[string]any{ + "http": "http://proxy.example.com:8080", + "https": "https://proxy.example.com:8443", + "noProxy": "localhost,127.0.0.1,.cluster.local", + }, + "proxyEnabled": true, + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: proxy-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + proxy: + http: "http://proxy.example.com:8080" + https: "https://proxy.example.com:8443" + noProxy: "localhost,127.0.0.1,.cluster.local" + optionalValues: + - when: "true" + values: + proxyEnabled: true`), + }, + }, + }, + { + name: "chart archive not found", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: missing-chart +spec: + chart: + name: nonexistent + chartVersion: "1.0.0" + values: + replicaCount: "1"`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), // Different chart + }, + configValues: types.AppConfigValues{}, + expectError: true, + errorContains: "find chart archive for missing-chart", + }, + { + name: "invalid when condition in optional values", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: invalid-when-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + replicaCount: "1" + optionalValues: + - when: "not-a-boolean-value" + values: + debug: true`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + }, + configValues: types.AppConfigValues{}, + expectError: true, + errorContains: "generate helm values for chart invalid-when-chart", + }, + { + name: "chart with mixed when conditions", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: mixed-conditions-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + replicaCount: "1" + optionalValues: + - when: '{{repl ConfigOption "enable_persistence"}}' + values: + persistence: + enabled: true + - when: '{{repl ConfigOption "disable_monitoring"}}' + values: + monitoring: + enabled: false`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + }, + configValues: types.AppConfigValues{ + "enable_persistence": {Value: "true"}, + "disable_monitoring": {Value: "false"}, + }, + expectError: false, + expected: []types.InstallableHelmChart{ + { + Archive: createTestChartArchive(t, "nginx", "1.0.0"), + Values: map[string]any{ + "replicaCount": "1", // from base values + "persistence": map[string]any{ + "enabled": true, // from optional values (when=true) + }, + // monitoring should NOT be present (when condition evaluated to false) + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: mixed-conditions-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + replicaCount: "1" + optionalValues: + - when: "true" + values: + persistence: + enabled: true + - when: "false" + values: + monitoring: + enabled: false`), + }, + }, + }, + { + name: "nil helm chart CRs", + helmChartCRs: nil, + chartArchives: [][]byte{}, + configValues: types.AppConfigValues{}, + expectError: false, + expected: nil, + }, + { + name: "skip nil helm chart CR in collection", + helmChartCRs: [][]byte{ + nil, + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: valid-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + replicaCount: "2"`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + }, + configValues: types.AppConfigValues{}, + expectError: false, + expected: []types.InstallableHelmChart{ + { + Archive: createTestChartArchive(t, "nginx", "1.0.0"), + Values: map[string]any{ + "replicaCount": "2", + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: valid-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + replicaCount: "2"`), + }, + }, + }, + { + name: "chart with registry template functions - airgap mode", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: registry-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + image: + repository: '{{repl HasLocalRegistry | ternary LocalRegistryHost "proxy.replicated.com"}}/{{repl HasLocalRegistry | ternary LocalRegistryNamespace "external/path"}}/nginx' + tag: "1.20.0" + imagePullSecrets: + - name: '{{repl ImagePullSecretName}}' + registry: + host: '{{repl LocalRegistryHost}}' + address: '{{repl LocalRegistryAddress}}' + namespace: '{{repl LocalRegistryNamespace}}' + secret: '{{repl LocalRegistryImagePullSecret}}' + optionalValues: + - when: '{{repl HasLocalRegistry}}' + values: + airgapMode: true`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + }, + configValues: types.AppConfigValues{}, + registrySettings: &types.RegistrySettings{ + HasLocalRegistry: true, + LocalRegistryHost: "10.128.0.11:5000", + LocalRegistryAddress: "10.128.0.11:5000/myapp", + LocalRegistryNamespace: "myapp", + ImagePullSecretName: "test-app-registry", + ImagePullSecretValue: "dGVzdC1zZWNyZXQtdmFsdWU=", + }, + expectError: false, + expected: []types.InstallableHelmChart{ + { + Archive: createTestChartArchive(t, "nginx", "1.0.0"), + Values: map[string]any{ + "image": map[string]any{ + "repository": "10.128.0.11:5000/myapp/nginx", + "tag": "1.20.0", + }, + "imagePullSecrets": []any{ + map[string]any{"name": "test-app-registry"}, + }, + "registry": map[string]any{ + "host": "10.128.0.11:5000", + "address": "10.128.0.11:5000/myapp", + "namespace": "myapp", + "secret": "dGVzdC1zZWNyZXQtdmFsdWU=", + }, + "airgapMode": true, + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: registry-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + values: + image: + repository: "10.128.0.11:5000/myapp/nginx" + tag: "1.20.0" + imagePullSecrets: + - name: "test-app-registry" + registry: + host: "10.128.0.11:5000" + address: "10.128.0.11:5000/myapp" + namespace: "myapp" + secret: "dGVzdC1zZWNyZXQtdmFsdWU=" + optionalValues: + - when: "true" + values: + airgapMode: true`), + }, + }, + }, + { + name: "charts sorted by weight - negative, zero, positive", + helmChartCRs: [][]byte{ + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: positive-weight-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + weight: 100 + values: + name: "positive"`), + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: no-weight-chart +spec: + chart: + name: redis + chartVersion: "2.0.0" + values: + name: "zero"`), // No weight specified, defaults to 0 + []byte(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: negative-weight-chart +spec: + chart: + name: postgresql + chartVersion: "1.0.0" + weight: -10 + values: + name: "negative"`), + }, + chartArchives: [][]byte{ + createTestChartArchive(t, "nginx", "1.0.0"), + createTestChartArchive(t, "redis", "2.0.0"), + createTestChartArchive(t, "postgresql", "1.0.0"), + }, + configValues: types.AppConfigValues{}, + expectError: false, + expected: []types.InstallableHelmChart{ + // Should be sorted by weight: postgresql (-10), redis (0), nginx (100) + { + Archive: createTestChartArchive(t, "postgresql", "1.0.0"), + Values: map[string]any{ + "name": "negative", + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: negative-weight-chart +spec: + chart: + name: postgresql + chartVersion: "1.0.0" + weight: -10 + values: + name: "negative"`), + }, + { + Archive: createTestChartArchive(t, "redis", "2.0.0"), + Values: map[string]any{ + "name": "zero", + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: no-weight-chart +spec: + chart: + name: redis + chartVersion: "2.0.0" + values: + name: "zero"`), + }, + { + Archive: createTestChartArchive(t, "nginx", "1.0.0"), + Values: map[string]any{ + "name": "positive", + }, + CR: createHelmChartCRFromYAML(`apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: positive-weight-chart +spec: + chart: + name: nginx + chartVersion: "1.0.0" + weight: 100 + values: + name: "positive"`), + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create release data + releaseData := &release.ReleaseData{ + HelmChartCRs: tt.helmChartCRs, + HelmChartArchives: tt.chartArchives, + } + + // Create manager + config := createTestConfig() + manager, err := NewAppReleaseManager( + config, + WithReleaseData(releaseData), + WithHelmClient(&helm.MockClient{}), + ) + require.NoError(t, err) + + // Execute the function + result, err := manager.ExtractInstallableHelmCharts(context.Background(), tt.configValues, tt.proxySpec, tt.registrySettings) + + // Check error expectation + if tt.expectError { + require.Error(t, err) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + assert.Nil(t, result) + return + } + + require.NoError(t, err) + + // Validate expected results + assert.Equal(t, tt.expected, result) + }) + } +} + // Helper function to create HelmChart from YAML string func createHelmChartCRFromYAML(yamlStr string) *kotsv1beta2.HelmChart { var chart kotsv1beta2.HelmChart @@ -2101,6 +2833,8 @@ func createTestConfig() kotsv1beta1.Config { Name: "test_group", Items: []kotsv1beta1.ConfigItem{ {Name: "chart_name", Type: "text", Value: multitype.FromString("nginx")}, + {Name: "namespace", Type: "text", Value: multitype.FromString("default-namespace")}, + {Name: "release_name", Type: "text", Value: multitype.FromString("default-release-name")}, {Name: "image_tag", Type: "text", Value: multitype.FromString("1.20.0")}, {Name: "app_name", Type: "text", Value: multitype.FromString("myapp")}, {Name: "chart1_name", Type: "text", Value: multitype.FromString("nginx")}, @@ -2120,6 +2854,16 @@ func createTestConfig() kotsv1beta1.Config { {Name: "node_count", Type: "text", Value: multitype.FromString("3")}, {Name: "version_check_name", Type: "text", Value: multitype.FromString("Custom K8s Version Check")}, {Name: "resource_check_name", Type: "text", Value: multitype.FromString("Custom Node Resource Check")}, + // Additional items for ExtractInstallableHelmCharts test + {Name: "image_tag", Type: "text", Value: multitype.FromString("1.20.0")}, + {Name: "enable_ingress", Type: "text", Value: multitype.FromString("true")}, + {Name: "ingress_host", Type: "text", Value: multitype.FromString("nginx.example.com")}, + {Name: "skip_nginx", Type: "text", Value: multitype.FromString("true")}, + {Name: "frontend_replicas", Type: "text", Value: multitype.FromString("3")}, + {Name: "frontend_tag", Type: "text", Value: multitype.FromString("1.20.0")}, + {Name: "enable_ssl", Type: "text", Value: multitype.FromString("true")}, + {Name: "redis_persistence", Type: "text", Value: multitype.FromString("true")}, + {Name: "invalid_boolean", Type: "text", Value: multitype.FromString("not-a-boolean")}, }, }, }, diff --git a/api/internal/managers/app/release/util.go b/api/internal/managers/app/release/util.go index 4f2c282a65..e4f74e0229 100644 --- a/api/internal/managers/app/release/util.go +++ b/api/internal/managers/app/release/util.go @@ -62,7 +62,7 @@ func writeChartArchiveToTemp(chartArchive []byte) (string, error) { // Write the chart archive to the temporary file if _, err := tmpFile.Write(chartArchive); err != nil { - os.Remove(tmpFile.Name()) + _ = os.Remove(tmpFile.Name()) return "", fmt.Errorf("write chart archive: %w", err) } diff --git a/api/internal/managers/linux/infra/image_test.go b/api/internal/managers/linux/infra/image_test.go index 450d2f326a..d90c9aa7a6 100644 --- a/api/internal/managers/linux/infra/image_test.go +++ b/api/internal/managers/linux/infra/image_test.go @@ -10,8 +10,8 @@ import ( func Test_DestECImage(t *testing.T) { registryOps := &types.RegistrySettings{ - Host: "localhost:5000", - Namespace: "somebigbank", + LocalRegistryHost: "localhost:5000", + LocalRegistryNamespace: "somebigbank", } type args struct { @@ -29,7 +29,7 @@ func Test_DestECImage(t *testing.T) { registry: registryOps, srcImage: "411111111111.dkr.ecr.us-west-1.amazonaws.com/myrepo:v0.0.1", }, - want: fmt.Sprintf("%s/%s/embedded-cluster/myrepo:v0.0.1", registryOps.Host, registryOps.Namespace), + want: fmt.Sprintf("%s/%s/embedded-cluster/myrepo:v0.0.1", registryOps.LocalRegistryHost, registryOps.LocalRegistryNamespace), }, { name: "Quay image with tag", @@ -37,7 +37,7 @@ func Test_DestECImage(t *testing.T) { registry: registryOps, srcImage: "quay.io/someorg/debian:0.1", }, - want: fmt.Sprintf("%s/%s/embedded-cluster/debian:0.1", registryOps.Host, registryOps.Namespace), + want: fmt.Sprintf("%s/%s/embedded-cluster/debian:0.1", registryOps.LocalRegistryHost, registryOps.LocalRegistryNamespace), }, { name: "Quay image with digest", @@ -45,7 +45,7 @@ func Test_DestECImage(t *testing.T) { registry: registryOps, srcImage: "quay.io/someorg/debian@sha256:17c5f462c92fc39303e6363c65e074559f8d6a1354150027ed5053557e3298c5", }, - want: fmt.Sprintf("%s/%s/embedded-cluster/debian@sha256:17c5f462c92fc39303e6363c65e074559f8d6a1354150027ed5053557e3298c5", registryOps.Host, registryOps.Namespace), + want: fmt.Sprintf("%s/%s/embedded-cluster/debian@sha256:17c5f462c92fc39303e6363c65e074559f8d6a1354150027ed5053557e3298c5", registryOps.LocalRegistryHost, registryOps.LocalRegistryNamespace), }, { name: "Image with tag and digest", @@ -53,17 +53,17 @@ func Test_DestECImage(t *testing.T) { registry: registryOps, srcImage: "quay.io/someorg/debian:0.1@sha256:17c5f462c92fc39303e6363c65e074559f8d6a1354150027ed5053557e3298c5", }, - want: fmt.Sprintf("%s/%s/embedded-cluster/debian@sha256:17c5f462c92fc39303e6363c65e074559f8d6a1354150027ed5053557e3298c5", registryOps.Host, registryOps.Namespace), + want: fmt.Sprintf("%s/%s/embedded-cluster/debian@sha256:17c5f462c92fc39303e6363c65e074559f8d6a1354150027ed5053557e3298c5", registryOps.LocalRegistryHost, registryOps.LocalRegistryNamespace), }, { name: "No Namespace", args: args{ registry: &types.RegistrySettings{ - Host: "localhost:5000", + LocalRegistryHost: "localhost:5000", }, srcImage: "quay.io/someorg/debian:0.1", }, - want: fmt.Sprintf("%s/embedded-cluster/debian:0.1", registryOps.Host), + want: fmt.Sprintf("%s/embedded-cluster/debian:0.1", registryOps.LocalRegistryHost), }, } for _, tt := range tests { diff --git a/api/internal/managers/linux/infra/upgrade.go b/api/internal/managers/linux/infra/upgrade.go index 348eb98221..e8ce4b60d7 100644 --- a/api/internal/managers/linux/infra/upgrade.go +++ b/api/internal/managers/linux/infra/upgrade.go @@ -138,8 +138,8 @@ func (m *infraManager) getECArtifacts(registrySettings *types.RegistrySettings) } opts := ECArtifactOCIPathOptions{ - RegistryHost: registrySettings.Host, - RegistryNamespace: registrySettings.Namespace, + RegistryHost: registrySettings.LocalRegistryHost, + RegistryNamespace: registrySettings.LocalRegistryNamespace, ChannelID: airgapInfo.Spec.ChannelID, UpdateCursor: airgapInfo.Spec.UpdateCursor, VersionLabel: airgapInfo.Spec.VersionLabel, @@ -276,7 +276,7 @@ func destECImage(registrySettings *types.RegistrySettings, srcImage string) (str imageParts := strings.Split(srcImage, "/") lastPart := imageParts[len(imageParts)-1] - return path.Join(registrySettings.Host, registrySettings.Namespace, "embedded-cluster", lastPart), nil + return path.Join(registrySettings.LocalRegistryHost, registrySettings.LocalRegistryNamespace, "embedded-cluster", lastPart), nil } func (m *infraManager) upgradeAddOns(ctx context.Context, in *ecv1beta1.Installation) error { diff --git a/api/internal/managers/linux/installation/config.go b/api/internal/managers/linux/installation/config.go index eca339a3a1..6b53121f09 100644 --- a/api/internal/managers/linux/installation/config.go +++ b/api/internal/managers/linux/installation/config.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/replicatedhq/embedded-cluster/api/internal/clients" + "github.com/replicatedhq/embedded-cluster/api/internal/utils" "github.com/replicatedhq/embedded-cluster/api/types" ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" newconfig "github.com/replicatedhq/embedded-cluster/pkg-new/config" @@ -15,8 +16,10 @@ import ( "github.com/replicatedhq/embedded-cluster/pkg/addons/registry" "github.com/replicatedhq/embedded-cluster/pkg/netutils" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" + kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" corev1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" + kyaml "sigs.k8s.io/yaml" ) // GetConfig returns the resolved installation configuration, with the user provided values AND defaults applied @@ -229,11 +232,14 @@ func (m *installationManager) ConfigureHost(ctx context.Context, rc runtimeconfi return nil } -// CalculateRegistrySettings calculates registry settings for airgap installations (should be used for new installations) +// CalculateRegistrySettings calculates registry settings for both online and airgap installations func (m *installationManager) CalculateRegistrySettings(ctx context.Context, rc runtimeconfig.RuntimeConfig) (*types.RegistrySettings, error) { - // Only return registry settings for airgap installations if m.airgapBundle == "" { - return nil, nil + registrySettings, err := m.getOnlineRegistrySettings() + if err != nil { + return nil, fmt.Errorf("failed to get online registry settings: %w", err) + } + return registrySettings, nil } // Use runtime config as the authoritative source for service CIDR @@ -247,13 +253,14 @@ func (m *installationManager) CalculateRegistrySettings(ctx context.Context, rc // Construct registry host with port registryHost := fmt.Sprintf("%s:5000", registryIP) - // Get app namespace from release data - required for app preflights + // Get app slug for secret name if m.releaseData == nil || m.releaseData.ChannelRelease == nil || m.releaseData.ChannelRelease.AppSlug == "" { return nil, fmt.Errorf("release data with app slug is required for registry settings") } - appNamespace := m.releaseData.ChannelRelease.AppSlug + appSlug := m.releaseData.ChannelRelease.AppSlug // Construct full registry address with namespace + appNamespace := appSlug // registry namespace is the same as the app slug in linux target registryAddress := fmt.Sprintf("%s/%s", registryHost, appNamespace) // Get registry credentials @@ -267,22 +274,26 @@ func (m *installationManager) CalculateRegistrySettings(ctx context.Context, rc imagePullSecretValue := base64.StdEncoding.EncodeToString([]byte(authConfig)) return &types.RegistrySettings{ - HasLocalRegistry: true, - Host: registryHost, - Address: registryAddress, - Namespace: appNamespace, - Username: registryUsername, - Password: registryPassword, - ImagePullSecretName: "embedded-cluster-registry", - ImagePullSecretValue: imagePullSecretValue, + HasLocalRegistry: true, + LocalRegistryHost: registryHost, + LocalRegistryAddress: registryAddress, + LocalRegistryNamespace: appNamespace, + LocalRegistryUsername: registryUsername, + LocalRegistryPassword: registryPassword, + ImagePullSecretName: fmt.Sprintf("%s-registry", appSlug), + ImagePullSecretValue: imagePullSecretValue, }, nil } -// GetRegistrySettings reads registry settings from the cluster for airgap installations (should be used for upgrades) +// GetRegistrySettings reads registry settings from the cluster for airgap installations, and calculates them for online installations (should be used for upgrades) func (m *installationManager) GetRegistrySettings(ctx context.Context, rc runtimeconfig.RuntimeConfig) (*types.RegistrySettings, error) { // If no airgap bundle, no registry settings needed if m.airgapBundle == "" { - return nil, nil + registrySettings, err := m.getOnlineRegistrySettings() + if err != nil { + return nil, fmt.Errorf("failed to get online registry settings: %w", err) + } + return registrySettings, nil } if m.kcli == nil { @@ -350,22 +361,54 @@ func (m *installationManager) GetRegistrySettings(ctx context.Context, rc runtim if m.releaseData == nil || m.releaseData.ChannelRelease == nil || m.releaseData.ChannelRelease.AppSlug == "" { return nil, fmt.Errorf("release data with app slug is required for registry settings") } - appNamespace := m.releaseData.ChannelRelease.AppSlug + appSlug := m.releaseData.ChannelRelease.AppSlug // Construct full registry address with namespace + appNamespace := appSlug // registry namespace is the same as the app slug in linux target registryAddress := fmt.Sprintf("%s/%s", registryHost, appNamespace) // Use the full dockerconfigjson as the image pull secret value imagePullSecretValue := base64.StdEncoding.EncodeToString(dockerJson) return &types.RegistrySettings{ - HasLocalRegistry: true, - Host: registryHost, - Address: registryAddress, - Namespace: appNamespace, - Username: registryUsername, - Password: registryPassword, - ImagePullSecretName: "embedded-cluster-registry", + HasLocalRegistry: true, + LocalRegistryHost: registryHost, + LocalRegistryAddress: registryAddress, + LocalRegistryNamespace: appNamespace, + LocalRegistryUsername: registryUsername, + LocalRegistryPassword: registryPassword, + ImagePullSecretName: fmt.Sprintf("%s-registry", appSlug), + ImagePullSecretValue: imagePullSecretValue, + }, nil +} + +func (m *installationManager) getOnlineRegistrySettings() (*types.RegistrySettings, error) { + // Online mode: Use replicated proxy registry with license ID authentication + ecDomains := utils.GetDomains(m.releaseData) + + // Parse license from bytes + if len(m.license) == 0 { + return nil, fmt.Errorf("license is required for online registry settings") + } + license := &kotsv1beta1.License{} + if err := kyaml.Unmarshal(m.license, license); err != nil { + return nil, fmt.Errorf("parse license: %w", err) + } + + // Get app slug for secret name + if m.releaseData == nil || m.releaseData.ChannelRelease == nil || m.releaseData.ChannelRelease.AppSlug == "" { + return nil, fmt.Errorf("release data with app slug is required for registry settings") + } + appSlug := m.releaseData.ChannelRelease.AppSlug + + // Create auth config for both proxy and registry domains + authConfig := fmt.Sprintf(`{"auths":{"%s":{"username": "LICENSE_ID", "password": "%s"},"%s":{"username": "LICENSE_ID", "password": "%s"}}}`, + ecDomains.ProxyRegistryDomain, license.Spec.LicenseID, ecDomains.ReplicatedRegistryDomain, license.Spec.LicenseID) + imagePullSecretValue := base64.StdEncoding.EncodeToString([]byte(authConfig)) + + return &types.RegistrySettings{ + HasLocalRegistry: false, + ImagePullSecretName: fmt.Sprintf("%s-registry", appSlug), ImagePullSecretValue: imagePullSecretValue, }, nil } diff --git a/api/internal/managers/linux/installation/config_test.go b/api/internal/managers/linux/installation/config_test.go index 5648973890..57d1836506 100644 --- a/api/internal/managers/linux/installation/config_test.go +++ b/api/internal/managers/linux/installation/config_test.go @@ -2,17 +2,22 @@ package installation import ( "context" + "encoding/base64" "errors" + "fmt" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/replicatedhq/embedded-cluster/api/internal/utils" "github.com/replicatedhq/embedded-cluster/api/types" ecv1beta1 "github.com/replicatedhq/embedded-cluster/kinds/apis/v1beta1" "github.com/replicatedhq/embedded-cluster/pkg-new/hostutils" + "github.com/replicatedhq/embedded-cluster/pkg/addons/registry" + "github.com/replicatedhq/embedded-cluster/pkg/release" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" + kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + kyaml "sigs.k8s.io/yaml" ) func TestValidateConfig(t *testing.T) { @@ -603,3 +608,177 @@ func TestConfigureHost(t *testing.T) { }) } } + +func TestCalculateRegistrySettings(t *testing.T) { + // Helper to create a test license + createTestLicense := func(licenseID, appSlug string) []byte { + license := kotsv1beta1.License{ + Spec: kotsv1beta1.LicenseSpec{ + LicenseID: licenseID, + AppSlug: appSlug, + }, + } + licenseBytes, _ := kyaml.Marshal(license) + return licenseBytes + } + + // Helper to create test release data + createTestReleaseData := func(appSlug string, domains *ecv1beta1.Domains) *release.ReleaseData { + releaseData := &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + AppSlug: appSlug, + }, + } + if domains != nil { + releaseData.EmbeddedClusterConfig = &ecv1beta1.Config{ + Spec: ecv1beta1.ConfigSpec{ + Domains: *domains, + }, + } + } + return releaseData + } + + // Helper to create runtime config + createTestRuntimeConfig := func() runtimeconfig.RuntimeConfig { + return runtimeconfig.New(&ecv1beta1.RuntimeConfigSpec{ + Network: ecv1beta1.NetworkSpec{ + ServiceCIDR: "10.96.0.0/12", + }, + }) + } + + tests := []struct { + name string + license []byte + releaseData *release.ReleaseData + airgapBundle string + expectedResult *types.RegistrySettings + expectedError string + }{ + { + name: "online mode with default domains", + license: createTestLicense("test-license-123", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "", // Online mode + expectedResult: &types.RegistrySettings{ + HasLocalRegistry: false, + ImagePullSecretName: "test-app-registry", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(`{"auths":{"proxy.replicated.com":{"username": "LICENSE_ID", "password": "test-license-123"},"registry.replicated.com":{"username": "LICENSE_ID", "password": "test-license-123"}}}`)), + }, + }, + { + name: "online mode with custom domains", + license: createTestLicense("custom-license-456", "custom-app"), + releaseData: createTestReleaseData("custom-app", &ecv1beta1.Domains{ + ProxyRegistryDomain: "custom-proxy.example.com", + ReplicatedRegistryDomain: "custom-registry.example.com", + }), + airgapBundle: "", // Online mode + expectedResult: &types.RegistrySettings{ + HasLocalRegistry: false, + ImagePullSecretName: "custom-app-registry", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(`{"auths":{"custom-proxy.example.com":{"username": "LICENSE_ID", "password": "custom-license-456"},"custom-registry.example.com":{"username": "LICENSE_ID", "password": "custom-license-456"}}}`)), + }, + }, + { + name: "online mode missing license", + license: nil, + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "", // Online mode + expectedError: "license is required for online registry settings", + }, + { + name: "online mode empty license", + license: []byte{}, + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "", // Online mode + expectedError: "license is required for online registry settings", + }, + { + name: "online mode invalid license format", + license: []byte("invalid yaml"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "", // Online mode + expectedError: "parse license:", + }, + { + name: "online mode missing release data", + license: createTestLicense("test-license", "test-app"), + releaseData: nil, + airgapBundle: "", // Online mode + expectedError: "release data with app slug is required for registry settings", + }, + { + name: "online mode missing app slug", + license: createTestLicense("test-license", "test-app"), + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + AppSlug: "", // Empty app slug + }, + }, + airgapBundle: "", // Online mode + expectedError: "release data with app slug is required for registry settings", + }, + { + name: "airgap mode", + license: createTestLicense("test-license", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "test-bundle.tar", + expectedResult: &types.RegistrySettings{ + HasLocalRegistry: true, + LocalRegistryHost: "10.96.0.11:5000", + LocalRegistryAddress: "10.96.0.11:5000/test-app", + LocalRegistryNamespace: "test-app", + LocalRegistryUsername: "embedded-cluster", + LocalRegistryPassword: registry.GetRegistryPassword(), + ImagePullSecretName: "test-app-registry", + ImagePullSecretValue: func() string { + authString := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("embedded-cluster:%s", registry.GetRegistryPassword()))) + return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"auths":{"10.96.0.11:5000":{"username": "embedded-cluster", "password": "%s", "auth": "%s"}}}`, registry.GetRegistryPassword(), authString))) + }(), + }, + }, + { + name: "airgap mode missing release data", + license: createTestLicense("test-license", "test-app"), + releaseData: nil, + airgapBundle: "test-bundle.tar", + expectedError: "release data with app slug is required for registry settings", + }, + { + name: "airgap mode missing app slug", + license: createTestLicense("test-license", "test-app"), + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + AppSlug: "", // Empty app slug + }, + }, + airgapBundle: "test-bundle.tar", + expectedError: "release data with app slug is required for registry settings", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rc := createTestRuntimeConfig() + + manager := NewInstallationManager( + WithLicense(tt.license), + WithReleaseData(tt.releaseData), + WithAirgapBundle(tt.airgapBundle), + ) + + result, err := manager.CalculateRegistrySettings(context.Background(), rc) + + if tt.expectedError != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedError) + assert.Nil(t, result) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedResult, result) + } + }) + } +} diff --git a/api/internal/store/app/install/store.go b/api/internal/store/app/install/store.go index 407833e3af..fa9668fd3c 100644 --- a/api/internal/store/app/install/store.go +++ b/api/internal/store/app/install/store.go @@ -21,6 +21,8 @@ type Store interface { SetStatusDesc(desc string) error AddLogs(logs string) error GetLogs() (string, error) + SetComponentStatus(componentName string, status types.Status) error + RegisterComponents(componentNames []string) error } // memoryStore is an in-memory implementation of Store @@ -115,3 +117,42 @@ func (s *memoryStore) GetLogs() (string, error) { defer s.mu.RUnlock() return s.appInstall.Logs, nil } + +// SetComponentStatus sets the status of a specific component +func (s *memoryStore) SetComponentStatus(componentName string, status types.Status) error { + s.mu.Lock() + defer s.mu.Unlock() + + // Find and update the component + for i := range s.appInstall.Components { + if s.appInstall.Components[i].Name == componentName { + s.appInstall.Components[i].Status = status + return nil + } + } + + return fmt.Errorf("component %s not found", componentName) +} + +// RegisterComponents initializes the components list with the given component names +func (s *memoryStore) RegisterComponents(componentNames []string) error { + s.mu.Lock() + defer s.mu.Unlock() + + // Clear existing components + s.appInstall.Components = make([]types.AppComponent, 0, len(componentNames)) + + // Initialize each component with pending status + for _, name := range componentNames { + s.appInstall.Components = append(s.appInstall.Components, types.AppComponent{ + Name: name, + Status: types.Status{ + State: types.StatePending, + Description: "", + LastUpdated: time.Now(), + }, + }) + } + + return nil +} diff --git a/api/internal/store/app/install/store_mock.go b/api/internal/store/app/install/store_mock.go index 689373fda1..06557cc1d2 100644 --- a/api/internal/store/app/install/store_mock.go +++ b/api/internal/store/app/install/store_mock.go @@ -53,3 +53,15 @@ func (m *MockStore) GetLogs() (string, error) { args := m.Called() return args.Get(0).(string), args.Error(1) } + +// SetComponentStatus mocks the SetComponentStatus method +func (m *MockStore) SetComponentStatus(componentName string, status types.Status) error { + args := m.Called(componentName, status) + return args.Error(0) +} + +// RegisterComponents mocks the RegisterComponents method +func (m *MockStore) RegisterComponents(componentNames []string) error { + args := m.Called(componentNames) + return args.Error(0) +} diff --git a/api/internal/store/app/install/store_test.go b/api/internal/store/app/install/store_test.go index e83aae151a..9492b474ac 100644 --- a/api/internal/store/app/install/store_test.go +++ b/api/internal/store/app/install/store_test.go @@ -280,3 +280,105 @@ func TestMemoryStore_DeepCopy(t *testing.T) { assert.Equal(t, "Original description", appInstall3.Status.Description) assert.Equal(t, "Original log\n", appInstall3.Logs) } + +func TestMemoryStore_RegisterComponents(t *testing.T) { + store := newMemoryStore() + + // Test registering components + componentNames := []string{"chart1", "chart2", "chart3"} + err := store.RegisterComponents(componentNames) + require.NoError(t, err) + + // Verify components were registered + appInstall, err := store.Get() + require.NoError(t, err) + require.Len(t, appInstall.Components, 3) + + for i, component := range appInstall.Components { + assert.Equal(t, componentNames[i], component.Name) + assert.Equal(t, types.StatePending, component.Status.State) + } +} + +func TestMemoryStore_SetComponentStatus(t *testing.T) { + store := newMemoryStore() + + // Register components first + componentNames := []string{"chart1", "chart2"} + err := store.RegisterComponents(componentNames) + require.NoError(t, err) + + // Test setting component status + status := types.Status{ + State: types.StateRunning, + Description: "Installing chart1", + LastUpdated: time.Now(), + } + err = store.SetComponentStatus("chart1", status) + require.NoError(t, err) + + // Verify component status was updated + appInstall, err := store.Get() + require.NoError(t, err) + assert.Equal(t, types.StateRunning, appInstall.Components[0].Status.State) + assert.Equal(t, "Installing chart1", appInstall.Components[0].Status.Description) + // Second component should remain unchanged + assert.Equal(t, types.StatePending, appInstall.Components[1].Status.State) +} + +func TestMemoryStore_SetComponentStatus_NonExistentComponent(t *testing.T) { + store := newMemoryStore() + + // Try to set status on non-existent component + status := types.Status{ + State: types.StateRunning, + Description: "Installing", + LastUpdated: time.Now(), + } + err := store.SetComponentStatus("nonexistent", status) + assert.Error(t, err) + assert.Contains(t, err.Error(), "component nonexistent not found") +} + +func TestMemoryStore_ComponentStatusConcurrency(t *testing.T) { + store := newMemoryStore() + + // Register components + componentNames := []string{"chart1", "chart2", "chart3"} + err := store.RegisterComponents(componentNames) + require.NoError(t, err) + + var wg sync.WaitGroup + numGoroutines := 10 + numOperations := 20 + + // Concurrent component status operations + wg.Add(numGoroutines * 2) + for i := 0; i < numGoroutines; i++ { + // Concurrent component status writes + go func(id int) { + defer wg.Done() + for j := 0; j < numOperations; j++ { + componentName := componentNames[j%len(componentNames)] + status := types.Status{ + State: types.StateRunning, + Description: "Concurrent update", + LastUpdated: time.Now(), + } + err := store.SetComponentStatus(componentName, status) + assert.NoError(t, err) + } + }(i) + + // Concurrent reads + go func(id int) { + defer wg.Done() + for j := 0; j < numOperations; j++ { + _, err := store.Get() + assert.NoError(t, err) + } + }(i) + } + + wg.Wait() +} diff --git a/api/pkg/template/registry.go b/api/pkg/template/registry.go index 8e1dce22c0..1c52ccafb5 100644 --- a/api/pkg/template/registry.go +++ b/api/pkg/template/registry.go @@ -15,7 +15,7 @@ func (e *Engine) localRegistryHost() string { if e.registrySettings == nil { return "" } - return e.registrySettings.Host + return e.registrySettings.LocalRegistryHost } // localRegistryAddress returns full registry address with namespace (e.g., "10.128.0.11:5000/myapp") @@ -23,7 +23,7 @@ func (e *Engine) localRegistryAddress() string { if e.registrySettings == nil { return "" } - return e.registrySettings.Address + return e.registrySettings.LocalRegistryAddress } // localRegistryNamespace returns the app-specific namespace for registry isolation @@ -31,7 +31,7 @@ func (e *Engine) localRegistryNamespace() string { if e.registrySettings == nil { return "" } - return e.registrySettings.Namespace + return e.registrySettings.LocalRegistryNamespace } // imagePullSecretName returns the standardized image pull secret name diff --git a/api/pkg/template/registry_test.go b/api/pkg/template/registry_test.go index 40799242dd..bca6c33ec5 100644 --- a/api/pkg/template/registry_test.go +++ b/api/pkg/template/registry_test.go @@ -67,14 +67,14 @@ func TestEngine_LocalRegistryHost(t *testing.T) { { name: "empty host returns empty string", registrySettings: &types.RegistrySettings{ - Host: "", + LocalRegistryHost: "", }, expectedResult: "", }, { name: "host with port returns host", registrySettings: &types.RegistrySettings{ - Host: "10.128.0.11:5000", + LocalRegistryHost: "10.128.0.11:5000", }, expectedResult: "10.128.0.11:5000", }, @@ -112,14 +112,14 @@ func TestEngine_LocalRegistryAddress(t *testing.T) { { name: "empty address returns empty string", registrySettings: &types.RegistrySettings{ - Address: "", + LocalRegistryAddress: "", }, expectedResult: "", }, { name: "address with namespace returns address", registrySettings: &types.RegistrySettings{ - Address: "10.128.0.11:5000/myapp", + LocalRegistryAddress: "10.128.0.11:5000/myapp", }, expectedResult: "10.128.0.11:5000/myapp", }, @@ -157,14 +157,14 @@ func TestEngine_LocalRegistryNamespace(t *testing.T) { { name: "empty namespace returns empty string", registrySettings: &types.RegistrySettings{ - Namespace: "", + LocalRegistryNamespace: "", }, expectedResult: "", }, { name: "namespace returns namespace", registrySettings: &types.RegistrySettings{ - Namespace: "myapp", + LocalRegistryNamespace: "myapp", }, expectedResult: "myapp", }, @@ -209,9 +209,9 @@ func TestEngine_ImagePullSecretName(t *testing.T) { { name: "secret name returns secret name", registrySettings: &types.RegistrySettings{ - ImagePullSecretName: "embedded-cluster-registry", + ImagePullSecretName: "test-app-registry", }, - expectedResult: "embedded-cluster-registry", + expectedResult: "test-app-registry", }, } @@ -281,12 +281,12 @@ func TestEngine_LocalRegistryImagePullSecret(t *testing.T) { // TestEngine_RegistryFunctionsIntegrated tests multiple registry functions in a single template func TestEngine_RegistryFunctionsIntegrated(t *testing.T) { registrySettings := &types.RegistrySettings{ - HasLocalRegistry: true, - Host: "10.128.0.11:5000", - Address: "10.128.0.11:5000/myapp", - Namespace: "myapp", - ImagePullSecretName: "embedded-cluster-registry", - ImagePullSecretValue: "eyJhdXRocyI6e319", + HasLocalRegistry: true, + LocalRegistryHost: "10.128.0.11:5000", + LocalRegistryAddress: "10.128.0.11:5000/myapp", + LocalRegistryNamespace: "myapp", + ImagePullSecretName: "test-app-registry", + ImagePullSecretValue: "eyJhdXRocyI6e319", } tests := []struct { @@ -312,7 +312,7 @@ func TestEngine_RegistryFunctionsIntegrated(t *testing.T) { { name: "image pull secret name in yaml", template: "- name: '{{repl ImagePullSecretName }}'", - expectedResult: "- name: 'embedded-cluster-registry'", + expectedResult: "- name: 'test-app-registry'", }, } diff --git a/api/types/app.go b/api/types/app.go index b47c5e8ceb..bd5d00e444 100644 --- a/api/types/app.go +++ b/api/types/app.go @@ -2,6 +2,7 @@ package types import ( kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + kotsv1beta2 "github.com/replicatedhq/kotskinds/apis/kots/v1beta2" ) // AppConfig represents the configuration for an app. This is an alias for the @@ -23,8 +24,23 @@ type AppConfigValues map[string]AppConfigValue // AppInstall represents the current state of app installation type AppInstall struct { - Status Status `json:"status"` - Logs string `json:"logs"` + Components []AppComponent `json:"components"` + Status Status `json:"status"` + Logs string `json:"logs"` +} + +// AppComponent represents an individual chart component within the app +// Following the same schema pattern as types.InfraComponent +type AppComponent struct { + Name string `json:"name"` // Chart name + Status Status `json:"status"` // Uses existing Status type +} + +// InstallableHelmChart represents a Helm chart with pre-processed values ready for installation +type InstallableHelmChart struct { + Archive []byte + Values map[string]any + CR *kotsv1beta2.HelmChart } // ConvertToAppConfigValues converts kots ConfigValues to AppConfigValues format diff --git a/api/types/registry.go b/api/types/registry.go index a0a3aa0ebe..f79b56f28a 100644 --- a/api/types/registry.go +++ b/api/types/registry.go @@ -5,20 +5,20 @@ type RegistrySettings struct { // HasLocalRegistry indicates if a local registry is available (airgap installations) HasLocalRegistry bool `json:"hasLocalRegistry"` - // Host is the registry host with port (e.g., "10.128.0.11:5000") - Host string `json:"host"` + // LocalRegistryHost is the registry host with port (e.g., "10.128.0.11:5000") + LocalRegistryHost string `json:"localRegistryHost"` - // Address is the full registry address with namespace (e.g., "10.128.0.11:5000/myapp") - Address string `json:"address"` + // LocalRegistryAddress is the full registry address with namespace (e.g., "10.128.0.11:5000/myapp") + LocalRegistryAddress string `json:"localRegistryAddress"` - // Namespace is the app-specific namespace for registry isolation - Namespace string `json:"namespace"` + // LocalRegistryNamespace is the app-specific namespace for registry isolation + LocalRegistryNamespace string `json:"localRegistryNamespace"` - // Username is the registry authentication username - Username string `json:"username"` + // LocalRegistryUsername is the registry authentication username + LocalRegistryUsername string `json:"localRegistryUsername"` - // Password is the registry authentication password - Password string `json:"password"` + // LocalRegistryPassword is the registry authentication password + LocalRegistryPassword string `json:"localRegistryPassword"` // ImagePullSecretName is the standardized image pull secret name ImagePullSecretName string `json:"imagePullSecretName"` diff --git a/cmd/buildtools/utils.go b/cmd/buildtools/utils.go index 727c6c7fbb..16352abd50 100644 --- a/cmd/buildtools/utils.go +++ b/cmd/buildtools/utils.go @@ -355,7 +355,7 @@ func GetGreatestTagFromRegistry(ctx context.Context, ref string, constraints *se func LatestChartVersion(ctx context.Context, hcli helm.Client, repo *repo.Entry, name string) (string, error) { logrus.Infof("adding helm repo %s", repo.Name) - err := hcli.AddRepoBin(ctx, repo) + err := hcli.AddRepo(ctx, repo) if err != nil { return "", fmt.Errorf("add helm repo: %w", err) } @@ -479,7 +479,7 @@ func MirrorChart(ctx context.Context, hcli helm.Client, repo *repo.Entry, name, logrus.Infof("downloaded %s chart: %s", name, chpath) defer os.Remove(chpath) - err = hcli.AddRepoBin(ctx, repo) + err = hcli.AddRepo(ctx, repo) if err != nil { return fmt.Errorf("add helm repo: %w", err) } @@ -539,7 +539,6 @@ func NewHelm() (helm.Client, error) { HelmPath: "helm", // use the helm binary in PATH KubernetesEnvSettings: helmcli.New(), // use the default env settings from helm K8sVersion: sv.Original(), - Writer: logrus.New().Writer(), }) } diff --git a/dev/dockerfiles/local-artifact-mirror/Dockerfile.ttlsh b/dev/dockerfiles/local-artifact-mirror/Dockerfile.ttlsh index 81736c413f..1375ed5dcb 100644 --- a/dev/dockerfiles/local-artifact-mirror/Dockerfile.ttlsh +++ b/dev/dockerfiles/local-artifact-mirror/Dockerfile.ttlsh @@ -1,4 +1,4 @@ -FROM golang:1.25.4 AS build +FROM golang:1.25.3 AS build WORKDIR /app diff --git a/dev/dockerfiles/operator/Dockerfile.local b/dev/dockerfiles/operator/Dockerfile.local index cd5d0b9f4f..3ee9d985ed 100644 --- a/dev/dockerfiles/operator/Dockerfile.local +++ b/dev/dockerfiles/operator/Dockerfile.local @@ -1,4 +1,4 @@ -FROM golang:1.25.4-alpine AS build +FROM golang:1.25.3-alpine AS build RUN apk add --no-cache ca-certificates curl git make bash helm diff --git a/dev/dockerfiles/operator/Dockerfile.ttlsh b/dev/dockerfiles/operator/Dockerfile.ttlsh index 278a4b4fc2..1a4ff8457e 100644 --- a/dev/dockerfiles/operator/Dockerfile.ttlsh +++ b/dev/dockerfiles/operator/Dockerfile.ttlsh @@ -1,4 +1,4 @@ -FROM golang:1.25.4 AS build +FROM golang:1.25.3 AS build WORKDIR /app diff --git a/e2e/kots-release-install-v3/nginx-app-helm-v1beta2.yaml b/e2e/kots-release-install-v3/nginx-app-helm-v1beta2.yaml index 6037f0d2ce..a706fc4e89 100644 --- a/e2e/kots-release-install-v3/nginx-app-helm-v1beta2.yaml +++ b/e2e/kots-release-install-v3/nginx-app-helm-v1beta2.yaml @@ -42,10 +42,10 @@ spec: configItemsConfigMapData: # Registry template functions verification has_local_registry: repl{{ HasLocalRegistry }} - local_registry_host: repl{{ HasLocalRegistry | ternary LocalRegistryHost "fallback-host" }} - local_registry_namespace: repl{{ HasLocalRegistry | ternary LocalRegistryNamespace "fallback-namespace" }} - local_registry_address: repl{{ HasLocalRegistry | ternary LocalRegistryAddress "fallback-address" }} - image_pull_secret_name: repl{{ HasLocalRegistry | ternary ImagePullSecretName "fallback-secret" }} + local_registry_host: repl{{ HasLocalRegistry | ternary LocalRegistryHost "ec-e2e-proxy.testcluster.net" }} + local_registry_namespace: repl{{ HasLocalRegistry | ternary LocalRegistryNamespace "anonymous/registry.replicated.com/library" }} + local_registry_address: repl{{ HasLocalRegistry | ternary LocalRegistryAddress "ec-e2e-proxy.testcluster.net/anonymous/registry.replicated.com/library" }} + image_pull_secret_name: repl{{ ImagePullSecretName }} image_pull_secret_value: repl{{ LocalRegistryImagePullSecret }} # Text items diff --git a/go.mod b/go.mod index c97442a750..9525c39ae9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/replicatedhq/embedded-cluster -go 1.25.4 +go 1.25.3 require ( github.com/AlecAivazis/survey/v2 v2.3.7 @@ -28,44 +28,44 @@ require ( github.com/gorilla/mux v1.8.1 github.com/gosimple/slug v1.15.0 github.com/hashicorp/go-retryablehttp v0.7.8 - github.com/jedib0t/go-pretty/v6 v6.7.5 + github.com/jedib0t/go-pretty/v6 v6.6.8 github.com/k0sproject/k0s v1.33.7-0.20251119112034-a756314d3a78 github.com/mattn/go-isatty v0.0.20 - github.com/ohler55/ojg v1.27.0 + github.com/ohler55/ojg v1.26.10 github.com/onsi/ginkgo/v2 v2.27.2 github.com/onsi/gomega v1.38.2 github.com/pkg/errors v0.9.1 github.com/replicatedhq/embedded-cluster/kinds v0.0.0 github.com/replicatedhq/embedded-cluster/utils v0.0.0 github.com/replicatedhq/kotskinds v0.0.0-20251024162531-2174a5b85a4d - github.com/replicatedhq/troubleshoot v0.123.15 + github.com/replicatedhq/troubleshoot v0.123.12 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.10.2 + github.com/spf13/cobra v1.10.1 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 github.com/swaggo/http-swagger/v2 v2.0.2 github.com/swaggo/swag/v2 v2.0.0-rc4 - github.com/tiendc/go-deepcopy v1.7.2 + github.com/tiendc/go-deepcopy v1.7.1 github.com/urfave/cli/v2 v2.27.7 - github.com/vmware-tanzu/velero v1.17.1 + github.com/vmware-tanzu/velero v1.17.0 go.uber.org/multierr v1.11.0 go.yaml.in/yaml/v3 v3.0.4 golang.org/x/crypto v0.45.0 golang.org/x/term v0.37.0 gopkg.in/yaml.v2 v2.4.0 gotest.tools v2.2.0+incompatible - helm.sh/helm/v3 v3.19.2 - k8s.io/api v0.34.2 - k8s.io/apiextensions-apiserver v0.34.2 - k8s.io/apimachinery v0.34.2 - k8s.io/cli-runtime v0.34.2 - k8s.io/client-go v0.34.2 - k8s.io/component-helpers v0.34.2 - k8s.io/kubectl v0.34.2 + helm.sh/helm/v3 v3.19.0 + k8s.io/api v0.34.1 + k8s.io/apiextensions-apiserver v0.34.1 + k8s.io/apimachinery v0.34.1 + k8s.io/cli-runtime v0.34.1 + k8s.io/client-go v0.34.1 + k8s.io/component-helpers v0.34.1 + k8s.io/kubectl v0.34.1 k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 oras.land/oras-go/v2 v2.6.0 - sigs.k8s.io/controller-runtime v0.22.4 + sigs.k8s.io/controller-runtime v0.22.3 sigs.k8s.io/yaml v1.6.0 ) @@ -77,9 +77,9 @@ replace ( require ( cel.dev/expr v0.24.0 // indirect cloud.google.com/go v0.121.6 // indirect - cloud.google.com/go/auth v0.17.0 // indirect + cloud.google.com/go/auth v0.16.5 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/compute/metadata v0.8.0 // indirect cloud.google.com/go/iam v1.5.2 // indirect cloud.google.com/go/monitoring v1.24.2 // indirect cloud.google.com/go/storage v1.56.2 // indirect @@ -88,7 +88,7 @@ require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.5.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect @@ -127,10 +127,10 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/chzyer/readline v1.5.1 // indirect - github.com/cilium/ebpf v0.20.0 // indirect + github.com/cilium/ebpf v0.19.0 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect - github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f // indirect - github.com/containerd/cgroups/v3 v3.1.1 // indirect + github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect + github.com/containerd/cgroups/v3 v3.0.5 // indirect github.com/containerd/containerd v1.7.29 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect @@ -145,19 +145,19 @@ require ( github.com/coreos/go-semver v0.3.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect - github.com/cyphar/filepath-securejoin v0.6.0 // indirect + github.com/cyphar/filepath-securejoin v0.5.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/distribution/v3 v3.0.0 // indirect - github.com/docker/cli v28.5.1+incompatible // indirect + github.com/docker/cli v28.3.2+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v28.5.1+incompatible // indirect + github.com/docker/docker v28.3.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.3 // indirect - github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.9.0 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.35.0 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect @@ -190,13 +190,13 @@ require ( github.com/google/cel-go v0.26.1 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/go-containerregistry v0.20.6 // indirect + github.com/google/go-containerregistry v0.20.3 // indirect github.com/google/go-intervals v0.0.2 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -209,7 +209,7 @@ require ( github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter v1.8.3 // indirect + github.com/hashicorp/go-getter v1.8.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect @@ -230,6 +230,7 @@ require ( github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect github.com/lib/pq v1.10.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e // indirect @@ -262,11 +263,10 @@ require ( github.com/muhlemmer/gu v0.3.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/nxadm/tail v1.4.11 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/opencontainers/runtime-spec v1.3.0 // indirect + github.com/opencontainers/runtime-spec v1.2.1 // indirect github.com/opencontainers/selinux v1.13.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect @@ -278,26 +278,24 @@ require ( github.com/proglottis/gpgme v0.1.4 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/common v0.67.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect - github.com/redis/go-redis/extra/redisotel/v9 v9.5.3 // indirect - github.com/redis/go-redis/v9 v9.10.0 // indirect github.com/rubenv/sql-migrate v1.8.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect - github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect github.com/segmentio/ksuid v1.0.4 // indirect github.com/sergi/go-diff v1.4.0 // indirect - github.com/shirou/gopsutil/v4 v4.25.10 // indirect + github.com/shirou/gopsutil/v4 v4.25.9 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/sigstore/fulcio v1.8.3 // indirect - github.com/sigstore/protobuf-specs v0.5.0 // indirect - github.com/sigstore/sigstore v1.10.0 // indirect + github.com/sigstore/fulcio v1.6.6 // indirect + github.com/sigstore/protobuf-specs v0.4.1 // indirect + github.com/sigstore/sigstore v1.9.5 // indirect github.com/smallstep/pkcs7 v0.1.1 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect github.com/stoewer/go-strcase v1.3.1 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -307,6 +305,7 @@ require ( github.com/swaggo/swag v1.8.1 // indirect github.com/sylabs/sif/v2 v2.21.1 // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect + github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/numcpus v0.10.0 // indirect github.com/ulikunitz/xz v0.5.15 // indirect @@ -318,6 +317,7 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + github.com/zeebo/errs v1.4.0 // indirect github.com/zitadel/logging v0.6.2 // indirect github.com/zitadel/oidc/v3 v3.45.0 // indirect github.com/zitadel/schema v1.3.1 // indirect @@ -326,9 +326,9 @@ require ( go.etcd.io/etcd/client/v3 v3.6.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.38.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect @@ -338,39 +338,39 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.8.0 // indirect - go.uber.org/zap v1.27.1 // indirect + go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b // indirect golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.31.0 // indirect - golang.org/x/time v0.14.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.38.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/api v0.256.0 // indirect + google.golang.org/api v0.249.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251014184007-4626949a642f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f // indirect + google.golang.org/grpc v1.76.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.34.2 // indirect + k8s.io/apiserver v0.34.1 // indirect k8s.io/cloud-provider v0.33.6 // indirect - k8s.io/component-base v0.34.2 // indirect + k8s.io/component-base v0.34.1 // indirect k8s.io/controller-manager v0.33.6 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kms v0.34.2 // indirect + k8s.io/kms v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect k8s.io/kubelet v0.34.1 // indirect k8s.io/kubernetes v1.34.1 // indirect - k8s.io/metrics v0.34.2 // indirect - oras.land/oras-go v1.2.7 // indirect + k8s.io/metrics v0.34.1 // indirect + oras.land/oras-go v1.2.6 // indirect periph.io/x/host/v3 v3.8.5 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect @@ -379,5 +379,3 @@ require ( sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) - -replace github.com/cyphar/filepath-securejoin => github.com/cyphar/filepath-securejoin v0.5.2 diff --git a/go.sum b/go.sum index 22775dc1ec..77def34cdf 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= -cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= -cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= +cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= +cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= +cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= @@ -29,27 +29,27 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 h1:E4MgwLBGeVB5f2MdcIVD3ELVAWpr+WD6MUe1i+tM/PA= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0/go.mod h1:Y2b/1clN4zsAoUd/pgNAQHjLDnTis/6ROkUfyob6psM= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= -github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= @@ -173,16 +173,16 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/cilium/ebpf v0.20.0 h1:atwWj9d3NffHyPZzVlx3hmw1on5CLe9eljR8VuHTwhM= -github.com/cilium/ebpf v0.20.0/go.mod h1:pzLjFymM+uZPLk/IXZUL63xdx5VXEo+enTzxkZXdycw= +github.com/cilium/ebpf v0.19.0 h1:Ro/rE64RmFBeA9FGjcTc+KmCeY6jXmryu6FfnzPRIao= +github.com/cilium/ebpf v0.19.0/go.mod h1:fLCgMo3l8tZmAdM3B2XqdFzXBpwkcSTroaVqN08OWVY= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= -github.com/containerd/cgroups/v3 v3.1.1 h1:ASZmQGfOHbRj43/1aMn5QcWIsv0R/AuHHDNCguRY0p0= -github.com/containerd/cgroups/v3 v3.1.1/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo= +github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE= github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= @@ -219,8 +219,8 @@ github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= -github.com/cyphar/filepath-securejoin v0.5.2 h1:w/T2bhKr4pgwG0SUGjU4S/Is9+zUknLh5ROTJLzWX8E= -github.com/cyphar/filepath-securejoin v0.5.2/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.5.1 h1:eYgfMq5yryL4fbWfkLpFFy2ukSELzaJOTaUTuh+oF48= +github.com/cyphar/filepath-securejoin v0.5.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -233,16 +233,16 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY= -github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY= +github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM= -github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= +github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= -github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= -github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= @@ -260,10 +260,10 @@ github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= -github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= -github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= +github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= +github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -287,7 +287,6 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= @@ -392,8 +391,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= -github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= +github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= +github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4= github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4= github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM= @@ -413,8 +412,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= @@ -434,10 +433,10 @@ github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99 h1:JYghRBlGCZyCF2wNUJ8W0cwaQdtpcssJ4CgC406g+WU= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= @@ -449,8 +448,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.8.3 h1:gIS+oTNv3kyYAvlUVgMR46MiG0bM0KuSON/KZEvRoRg= -github.com/hashicorp/go-getter v1.8.3/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4= +github.com/hashicorp/go-getter v1.8.2 h1:CGCK+bZQLl44PYiwJweVzfpjg7bBwtuXu3AGcLiod2o= +github.com/hashicorp/go-getter v1.8.2/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -459,6 +458,7 @@ github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVU github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru/arc/v2 v2.0.7 h1:QxkVTxwColcduO+LP7eJO56r2hFiG8zEbfAAzRv52KQ= github.com/hashicorp/golang-lru/arc/v2 v2.0.7/go.mod h1:Pe7gBlGdc8clY5LJ0LpJXMt5AmgmWNH1g+oFFVUHOEc= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -478,10 +478,12 @@ github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jedib0t/go-pretty/v6 v6.7.5 h1:9dJSWTJnsXJVVAbvxIFxeHf/JxoJd7GUl5o3UzhtuiM= -github.com/jedib0t/go-pretty/v6 v6.7.5/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc= +github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jeremija/gosubmit v0.2.8 h1:mmSITBz9JxVtu8eqbN+zmmwX7Ij2RidQxhcwRVI4wqA= github.com/jeremija/gosubmit v0.2.8/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= +github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= +github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= @@ -530,6 +532,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ= +github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= @@ -628,8 +632,8 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= -github.com/ohler55/ojg v1.27.0 h1:1JzdkMpDc/X9bzRaN1+8AFLnrSiFy96yDSaeACCGD5U= -github.com/ohler55/ojg v1.27.0/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= +github.com/ohler55/ojg v1.26.10 h1:qXq8A0AjzwvO+rKJWv9apNVWxyu3He8lgGZZ+AoEdLA= +github.com/ohler55/ojg v1.26.10/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -646,8 +650,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= -github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww= +github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE= github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= @@ -695,24 +699,24 @@ github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvM github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= -github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/common v0.67.1 h1:OTSON1P4DNxzTg4hmKCc37o4ZAZDv0cfXLkOt0oEowI= +github.com/prometheus/common v0.67.1/go.mod h1:RpmT9v35q2Y+lsieQsdOh5sXZ6ajUGC8NjZAmr8vb0Q= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= -github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3 h1:1/BDligzCa40GTllkDnY3Y5DTHuKCONbB2JcRyIfl20= -github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3/go.mod h1:3dZmcLn3Qw6FLlWASn1g4y+YO9ycEFUOM+bhBmzLVKQ= -github.com/redis/go-redis/extra/redisotel/v9 v9.5.3 h1:kuvuJL/+MZIEdvtb/kTBRiRgYaOmx1l+lYJyVdrRUOs= -github.com/redis/go-redis/extra/redisotel/v9 v9.5.3/go.mod h1:7f/FMrf5RRRVHXgfk7CzSVzXHiWeuOQUu2bsVqWoa+g= -github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs= -github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho= +github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= +github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= +github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/replicatedhq/kotskinds v0.0.0-20251024162531-2174a5b85a4d h1:N8t9W5SYs1MKPsuAp4PA5Haje4cOyCyubAq65qB1wzE= github.com/replicatedhq/kotskinds v0.0.0-20251024162531-2174a5b85a4d/go.mod h1:+k4PHo2wukoU9kdiKrqqgi89Wmj+9AiwppYGVK11zig= -github.com/replicatedhq/troubleshoot v0.123.15 h1:znJpO0oxln+5247mScmsovwhycDa+3Wm28ZCuWQ01eg= -github.com/replicatedhq/troubleshoot v0.123.15/go.mod h1:f9/bTb5p6LCgpQqqvbOqUwqTCkqGT8kc8wzZuFkX0xM= +github.com/replicatedhq/troubleshoot v0.123.12 h1:XbgZJMSwIHyf1lvxIRNwI9AVsRzcA7N3AWLPLSkrr+w= +github.com/replicatedhq/troubleshoot v0.123.12/go.mod h1:CKPCj8si77XuSL6sIAFdqtO23/eha159eEBlQF8HpVw= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= @@ -727,22 +731,22 @@ github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEV github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY= github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= -github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g= -github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU= +github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= +github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA= -github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM= +github.com/shirou/gopsutil/v4 v4.25.9 h1:JImNpf6gCVhKgZhtaAHJ0serfFGtlfIlSC08eaKdTrU= +github.com/shirou/gopsutil/v4 v4.25.9/go.mod h1:gxIxoC+7nQRwUl/xNhutXlD8lq+jxTgpIkEf3rADHL8= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sigstore/fulcio v1.8.3 h1:zkuAkRHbD53hhYGlBHHeAW4NRDrrTiDHumAbcfSyyFw= -github.com/sigstore/fulcio v1.8.3/go.mod h1:YxP7TTdn9H5Gg+dXOsu61X36LLYxT2ZuvODhWelMNwA= -github.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY= -github.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= -github.com/sigstore/sigstore v1.10.0 h1:lQrmdzqlR8p9SCfWIpFoGUqdXEzJSZT2X+lTXOMPaQI= -github.com/sigstore/sigstore v1.10.0/go.mod h1:Ygq+L/y9Bm3YnjpJTlQrOk/gXyrjkpn3/AEJpmk1n9Y= +github.com/sigstore/fulcio v1.6.6 h1:XaMYX6TNT+8n7Npe8D94nyZ7/ERjEsNGFC+REdi/wzw= +github.com/sigstore/fulcio v1.6.6/go.mod h1:BhQ22lwaebDgIxVBEYOOqLRcN5+xOV+C9bh/GUXRhOk= +github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc= +github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= +github.com/sigstore/sigstore v1.9.5 h1:Wm1LT9yF4LhQdEMy5A2JeGRHTrAWGjT3ubE5JUSrGVU= +github.com/sigstore/sigstore v1.9.5/go.mod h1:VtxgvGqCmEZN9X2zhFSOkfXxvKUjpy8RpUW39oCtoII= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -755,15 +759,15 @@ github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= -github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= -github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw= github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= @@ -809,8 +813,10 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44= -github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= +github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4= +github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= +github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= +github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= @@ -829,8 +835,8 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -github.com/vmware-tanzu/velero v1.17.1 h1:ldKeiTuUwkThOw7zrUucNA1NwnLG66zl13YetWAoE0I= -github.com/vmware-tanzu/velero v1.17.1/go.mod h1:3KTxuUN6Un38JzmYAX+8U6j2k6EexGoNNxa8jrJML8U= +github.com/vmware-tanzu/velero v1.17.0 h1:b+KLlBG+v1YKogP81nAFix2pgJBTmUrnVlXg+OfB5ao= +github.com/vmware-tanzu/velero v1.17.0/go.mod h1:BJRFKei89hSqrazQKiwv5YhhX871X1W1qPyo5OP09zw= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= @@ -844,6 +850,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zitadel/logging v0.6.2 h1:MW2kDDR0ieQynPZ0KIZPrh9ote2WkxfBif5QoARDQcU= github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4= github.com/zitadel/oidc/v3 v3.45.0 h1:SaVJ2kdcJi/zdEWWlAns+81VxmfdYX4E+2mWFVIH7Ec= @@ -870,14 +878,14 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk= -go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs= -go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4= go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= @@ -920,8 +928,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -978,8 +986,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= +golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1021,7 +1029,6 @@ golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1058,8 +1065,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1084,8 +1091,8 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= -google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= +google.golang.org/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w= +google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1093,18 +1100,18 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= -google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/api v0.0.0-20251014184007-4626949a642f h1:OiFuztEyBivVKDvguQJYWq1yDcfAHIID/FVrPR4oiI0= +google.golang.org/genproto/googleapis/api v0.0.0-20251014184007-4626949a642f/go.mod h1:kprOiu9Tr0JYyD6DORrc4Hfyk3RFXqkQ3ctHEum3ZbM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f h1:1FTH6cpXFsENbPR5Bu8NQddPSaUUE6NA2XdZdDSAJK4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1145,57 +1152,57 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -helm.sh/helm/v3 v3.19.2 h1:psQjaM8aIWrSVEly6PgYtLu/y6MRSmok4ERiGhZmtUY= -helm.sh/helm/v3 v3.19.2/go.mod h1:gX10tB5ErM+8fr7bglUUS/UfTOO8UUTYWIBH1IYNnpE= +helm.sh/helm/v3 v3.19.0 h1:krVyCGa8fa/wzTZgqw0DUiXuRT5BPdeqE/sQXujQ22k= +helm.sh/helm/v3 v3.19.0/go.mod h1:Lk/SfzN0w3a3C3o+TdAKrLwJ0wcZ//t1/SDXAvfgDdc= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= -k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= -k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo= -k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE= -k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= -k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.2 h1:2/yu8suwkmES7IzwlehAovo8dDE07cFRC7KMDb1+MAE= -k8s.io/apiserver v0.34.2/go.mod h1:gqJQy2yDOB50R3JUReHSFr+cwJnL8G1dzTA0YLEqAPI= -k8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE= -k8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4= -k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= -k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= +k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= +k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= +k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= +k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= +k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= +k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= +k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= +k8s.io/cli-runtime v0.34.1 h1:btlgAgTrYd4sk8vJTRG6zVtqBKt9ZMDeQZo2PIzbL7M= +k8s.io/cli-runtime v0.34.1/go.mod h1:aVA65c+f0MZiMUPbseU/M9l1Wo2byeaGwUuQEQVVveE= +k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= +k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= k8s.io/cloud-provider v0.33.6 h1:XLt8UhgsZgou8Ua6KQrnnHGS3lpZRHlbNhnO4kPyO0c= k8s.io/cloud-provider v0.33.6/go.mod h1:I08jJ7obAWjv9z5ZcabdOV3aizfo7ru495xuxlHQw84= -k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= -k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= -k8s.io/component-helpers v0.34.2 h1:RIUGDdU+QFzeVKLZ9f05sXTNAtJrRJ3bnbMLrogCrvM= -k8s.io/component-helpers v0.34.2/go.mod h1:pLi+GByuRTeFjjcezln8gHL7LcT6HImkwVQ3A2SQaEE= +k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= +k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= +k8s.io/component-helpers v0.34.1 h1:gWhH3CCdwAx5P3oJqZKb4Lg5FYZTWVbdWtOI8n9U4XY= +k8s.io/component-helpers v0.34.1/go.mod h1:4VgnUH7UA/shuBur+OWoQC0xfb69sy/93ss0ybZqm3c= k8s.io/controller-manager v0.33.6 h1:md2KM5RabX7KTTly1KmNDbNr5YJvLtse7sJOX2we8tI= k8s.io/controller-manager v0.33.6/go.mod h1:dtCyZIG+CNFI8hY/lQY7ZAOO20VxGm+lpi1do2fmLGY= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kms v0.34.2 h1:91rj4MDZLyIT9KxG8J5/CcMH666Z88CF/xJQeuPfJc8= -k8s.io/kms v0.34.2/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= +k8s.io/kms v0.34.1 h1:iCFOvewDPzWM9fMTfyIPO+4MeuZ0tcZbugxLNSHFG4w= +k8s.io/kms v0.34.1/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= -k8s.io/kubectl v0.34.2 h1:+fWGrVlDONMUmmQLDaGkQ9i91oszjjRAa94cr37hzqA= -k8s.io/kubectl v0.34.2/go.mod h1:X2KTOdtZZNrTWmUD4oHApJ836pevSl+zvC5sI6oO2YQ= +k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI= +k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A= k8s.io/kubelet v0.34.1 h1:doAaTA9/Yfzbdq/u/LveZeONp96CwX9giW6b+oHn4m4= k8s.io/kubelet v0.34.1/go.mod h1:PtV3Ese8iOM19gSooFoQT9iyRisbmJdAPuDImuccbbA= k8s.io/kubernetes v1.34.1 h1:F3p8dtpv+i8zQoebZeK5zBqM1g9x1aIdnA5vthvcuUk= k8s.io/kubernetes v1.34.1/go.mod h1:iu+FhII+Oc/1gGWLJcer6wpyih441aNFHl7Pvm8yPto= -k8s.io/metrics v0.34.2 h1:zao91FNDVPRGIiHLO2vqqe21zZVPien1goyzn0hsz90= -k8s.io/metrics v0.34.2/go.mod h1:Ydulln+8uZZctUM8yrUQX4rfq/Ay6UzsuXf24QJ37Vc= +k8s.io/metrics v0.34.1 h1:374Rexmp1xxgRt64Bi0TsjAM8cA/Y8skwCoPdjtIslE= +k8s.io/metrics v0.34.1/go.mod h1:Drf5kPfk2NJrlpcNdSiAAHn/7Y9KqxpRNagByM7Ei80= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go v1.2.7 h1:KF9rBAtKYMGB5gjgHV5XquUfYDER3ecQBEXjdI7KZWI= -oras.land/oras-go v1.2.7/go.mod h1:WVpIPbm82xjWT/GJU3TqZ0y9Ctj3DGco4wLYvGdOVvA= +oras.land/oras-go v1.2.6 h1:z8cmxQXBU8yZ4mkytWqXfo6tZcamPwjsuxYU81xJ8Lk= +oras.land/oras-go v1.2.6/go.mod h1:OVPc1PegSEe/K8YiLfosrlqlqTN9PUyFvOw5Y9gwrT8= oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o= periph.io/x/host/v3 v3.8.5 h1:g4g5xE1XZtDiGl1UAJaUur1aT7uNiFLMkyMEiZ7IHII= periph.io/x/host/v3 v3.8.5/go.mod h1:hPq8dISZIc+UNfWoRj+bPH3XEBQqJPdFdx218W92mdc= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 h1:XotDXzqvJ8Nx5eiZZueLpTuafJz8SiodgOemI+w87QU= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= -sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y= +sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= diff --git a/kinds/go.mod b/kinds/go.mod index 0474255554..f910224fb7 100644 --- a/kinds/go.mod +++ b/kinds/go.mod @@ -1,54 +1,52 @@ module github.com/replicatedhq/embedded-cluster/kinds -go 1.25.4 +go 1.25.3 require ( github.com/google/uuid v1.6.0 github.com/k0sproject/dig v0.4.0 - github.com/k0sproject/k0s v1.33.7-0.20251119112034-a756314d3a78 + github.com/k0sproject/k0s v1.33.5-0.20250819091818-6da1d9c31be6 github.com/stretchr/testify v1.11.1 go.yaml.in/yaml/v3 v3.0.4 - k8s.io/api v0.34.2 - k8s.io/apimachinery v0.34.2 - sigs.k8s.io/controller-runtime v0.22.4 + k8s.io/api v0.34.1 + k8s.io/apimachinery v0.34.1 + sigs.k8s.io/controller-runtime v0.22.3 sigs.k8s.io/yaml v1.6.0 ) require ( - github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/cyphar/filepath-securejoin v0.5.2 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kr/text v0.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/onsi/ginkgo/v2 v2.23.4 // indirect + github.com/onsi/gomega v1.36.3 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/pflag v1.0.9 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect helm.sh/helm/v3 v3.18.6 // indirect k8s.io/apiextensions-apiserver v0.34.1 // indirect - k8s.io/client-go v0.34.2 // indirect + k8s.io/client-go v0.34.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect diff --git a/kinds/go.sum b/kinds/go.sum index b11bec8cf2..1e0229136c 100644 --- a/kinds/go.sum +++ b/kinds/go.sum @@ -1,12 +1,11 @@ -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.5.2 h1:w/T2bhKr4pgwG0SUGjU4S/Is9+zUknLh5ROTJLzWX8E= -github.com/cyphar/filepath-securejoin v0.5.2/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -26,16 +25,16 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/k0sproject/dig v0.4.0 h1:yBxFUUxNXAMGBg6b7c6ypxdx/o3RmhoI5v5ABOw5tn0= github.com/k0sproject/dig v0.4.0/go.mod h1:rlZ7N7ZEcB4Fi96TPXkZ4dqyAiDWOGLapyL9YpZ7Qz4= -github.com/k0sproject/k0s v1.33.7-0.20251119112034-a756314d3a78 h1:lrA3V3mlmLKkHN01/f0lx4MF+x5ijYSsH2DdOhhwA1Y= -github.com/k0sproject/k0s v1.33.7-0.20251119112034-a756314d3a78/go.mod h1:MOvHATSyFUudgQCXg7fD6r86hqFljRqz43QvbIgrKYs= +github.com/k0sproject/k0s v1.33.5-0.20250819091818-6da1d9c31be6 h1:g5Jg/vHENLY1UnxDLkzcXLxxmSwLIxXIJNWlRAFPP6s= +github.com/k0sproject/k0s v1.33.5-0.20250819091818-6da1d9c31be6/go.mod h1:DUWIG4PCnk8w0muU+mlE5VZq2vvKrB//XBcYvHKVK54= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -52,10 +51,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= -github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -69,8 +68,8 @@ github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEV github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -84,8 +83,10 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -93,37 +94,37 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -138,20 +139,20 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= helm.sh/helm/v3 v3.18.6 h1:S/2CqcYnNfLckkHLI0VgQbxgcDaU3N4A/46E3n9wSNY= helm.sh/helm/v3 v3.18.6/go.mod h1:L/dXDR2r539oPlFP1PJqKAC1CUgqHJDLkxKpDGrWnyg= -k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= -k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= +k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= +k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= -k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= -k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= +k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= +k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= +k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= -sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y= +sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= diff --git a/operator/pkg/cli/upgrade_job.go b/operator/pkg/cli/upgrade_job.go index 7be1e0edab..6625fae969 100644 --- a/operator/pkg/cli/upgrade_job.go +++ b/operator/pkg/cli/upgrade_job.go @@ -64,6 +64,7 @@ func UpgradeJobCmd() *cobra.Command { } hcli, err := helm.NewClient(helm.HelmOptions{ + HelmPath: "helm", // use the helm binary in PATH K8sVersion: versions.K0sVersion, AirgapPath: airgapChartsPath, }) diff --git a/pkg/addons/adminconsole/integration/hostcabundle_test.go b/pkg/addons/adminconsole/integration/hostcabundle_test.go index 53b1faab48..1d7b4305f7 100644 --- a/pkg/addons/adminconsole/integration/hostcabundle_test.go +++ b/pkg/addons/adminconsole/integration/hostcabundle_test.go @@ -28,7 +28,10 @@ func TestHostCABundle(t *testing.T) { err := os.WriteFile(addon.HostCABundlePath, []byte("test"), 0644) require.NoError(t, err, "Failed to write CA bundle file") - hcli, err := helm.NewClient(helm.HelmOptions{}) + hcli, err := helm.NewClient(helm.HelmOptions{ + HelmPath: "helm", // use the helm binary in PATH + K8sVersion: "v1.33.0", + }) require.NoError(t, err, "NewClient should not return an error") err = addon.Install(context.Background(), t.Logf, nil, nil, hcli, ecv1beta1.Domains{}, nil) diff --git a/pkg/addons/adminconsole/integration/kubernetes_test.go b/pkg/addons/adminconsole/integration/kubernetes_test.go index 20c92f0d87..84e4163ed0 100644 --- a/pkg/addons/adminconsole/integration/kubernetes_test.go +++ b/pkg/addons/adminconsole/integration/kubernetes_test.go @@ -32,7 +32,10 @@ func TestKubernetes_Airgap(t *testing.T) { KotsadmNamespace: "my-app-namespace", } - hcli, err := helm.NewClient(helm.HelmOptions{}) + hcli, err := helm.NewClient(helm.HelmOptions{ + HelmPath: "helm", // use the helm binary in PATH + K8sVersion: "v1.33.0", + }) require.NoError(t, err, "NewClient should not return an error") err = addon.Install(context.Background(), t.Logf, nil, nil, hcli, ecv1beta1.Domains{}, nil) diff --git a/pkg/addons/adminconsole/integration/linux_test.go b/pkg/addons/adminconsole/integration/linux_test.go index d6db5c2944..3545d917dc 100644 --- a/pkg/addons/adminconsole/integration/linux_test.go +++ b/pkg/addons/adminconsole/integration/linux_test.go @@ -46,7 +46,10 @@ func TestLinux_Airgap(t *testing.T) { err := os.WriteFile(addon.HostCABundlePath, []byte("test"), 0644) require.NoError(t, err, "Failed to write CA bundle file") - hcli, err := helm.NewClient(helm.HelmOptions{}) + hcli, err := helm.NewClient(helm.HelmOptions{ + HelmPath: "helm", // use the helm binary in PATH + K8sVersion: "v1.33.0", + }) require.NoError(t, err, "NewClient should not return an error") err = addon.Install(context.Background(), t.Logf, nil, nil, hcli, ecv1beta1.Domains{}, nil) diff --git a/pkg/addons/adminconsole/static/metadata.yaml b/pkg/addons/adminconsole/static/metadata.yaml index 999ab370f2..dcaeb52ce3 100644 --- a/pkg/addons/adminconsole/static/metadata.yaml +++ b/pkg/addons/adminconsole/static/metadata.yaml @@ -5,26 +5,26 @@ # $ make buildtools # $ output/bin/buildtools update addon # -version: 1.129.1 +version: 1.128.3 location: oci://proxy.replicated.com/library/admin-console images: kotsadm: repo: proxy.replicated.com/anonymous/kotsadm/kotsadm tag: - amd64: v1.129.1-amd64@sha256:754079c15e62752a8d3e95f969979f90c804e6ba360c4a449a8064ef0f429f9e - arm64: v1.129.1-arm64@sha256:a8cc3a8b43fcede48c12350f29c421538170adde48d9be9a4ad96ea4b3007e22 + amd64: v1.128.3-amd64@sha256:a54b6bcc2623d8712df92f19cfd290c9b791cfd71c20ba7962aed6c5442754c8 + arm64: v1.128.3-arm64@sha256:646ae8d1987064e047f06de39ae6800f4ba617b1fec2ccb4a10b6a2a4623f39f kotsadm-migrations: repo: proxy.replicated.com/anonymous/kotsadm/kotsadm-migrations tag: - amd64: v1.129.1-amd64@sha256:173431dea760c2a0ba79b25b7c4d983bdfc97a25fc0ef17037bdd3eb231f6407 - arm64: v1.129.1-arm64@sha256:51ad1d1a24fca5b4b3905321f10379f246f6ded2a99807f4a02eb1bd6eb38629 + amd64: v1.128.3-amd64@sha256:d4a66c0d05216e06eb30be8bcf1d541cf9922e9985e8d3dddc10a6a230d1e913 + arm64: v1.128.3-arm64@sha256:ee54465951a3ca488d9ea1539261a59e46bec98bfa2084f98c6155614fc0a084 kurl-proxy: repo: proxy.replicated.com/anonymous/kotsadm/kurl-proxy tag: - amd64: v1.129.1-amd64@sha256:41e6e77919110366aaf0ae5966a065d5f3506ff32232c4f5959dad576605b9eb - arm64: v1.129.1-arm64@sha256:eec1fa1123c540b56b38575fef0b224ebfbb68a737265158b64a70c8f6154655 + amd64: v1.128.3-amd64@sha256:37a442ffdd1cff6b1d5909036a6e482356f74055a826e345e213b869f0c492d5 + arm64: v1.128.3-arm64@sha256:f743cabd0bea24e50e5b7878f94150190390b301f574098fb7bb09062c11b2d9 rqlite: repo: proxy.replicated.com/anonymous/kotsadm/rqlite tag: - amd64: 9.3.4-amd64@sha256:d67af54f63cc1fdab0e68e2d6de34dfd7a99c72f6df9d890c348198201904db9 - arm64: 9.3.4-arm64@sha256:ab44603a54dd821dc87ab4ff0c03e52dc527aa6dc760034a144baec496b1e118 + amd64: 9.1.3-r0-amd64@sha256:b156612409debdee543329e6b0736d29b7d57fe0946b8b202014426bc3a4c0de + arm64: 9.1.3-r0-arm64@sha256:e82e67661ae3fdcef06d3fda8a02362d7906edd7e8ed7a1941a5c382e128cf6b diff --git a/pkg/addons/adminconsole/values.go b/pkg/addons/adminconsole/values.go index 66d10f5461..87029ccd84 100644 --- a/pkg/addons/adminconsole/values.go +++ b/pkg/addons/adminconsole/values.go @@ -45,8 +45,7 @@ func (a *AdminConsole) GenerateHelmValues(ctx context.Context, kcli client.Clien copiedValues["embeddedClusterID"] = a.ClusterID copiedValues["embeddedClusterDataDir"] = a.DataDir copiedValues["embeddedClusterK0sDir"] = a.K0sDataDir - // TODO: enable this once we stop relying on KOTS to deploy the app - // copiedValues["isEmbeddedClusterV3"] = a.isV3() + copiedValues["isEmbeddedClusterV3"] = a.isV3() } copiedValues["isHA"] = a.IsHA diff --git a/pkg/addons/adminconsole/values_test.go b/pkg/addons/adminconsole/values_test.go index 0538381ab3..51bbd09058 100644 --- a/pkg/addons/adminconsole/values_test.go +++ b/pkg/addons/adminconsole/values_test.go @@ -112,8 +112,7 @@ func TestGenerateHelmValues_Target(t *testing.T) { assert.Equal(t, "123", values["embeddedClusterID"]) assert.Equal(t, dataDir, values["embeddedClusterDataDir"]) assert.Equal(t, filepath.Join(dataDir, "k0s"), values["embeddedClusterK0sDir"]) - // TODO: enable this once we stop relying on KOTS to deploy the app - // assert.Equal(t, true, values["isEmbeddedClusterV3"]) + assert.Equal(t, true, values["isEmbeddedClusterV3"]) assert.Contains(t, values["extraEnv"], map[string]interface{}{ "name": "SSL_CERT_CONFIGMAP", diff --git a/pkg/addons/embeddedclusteroperator/integration/hostcabundle_test.go b/pkg/addons/embeddedclusteroperator/integration/hostcabundle_test.go index 2ee9a572ef..b9690d7468 100644 --- a/pkg/addons/embeddedclusteroperator/integration/hostcabundle_test.go +++ b/pkg/addons/embeddedclusteroperator/integration/hostcabundle_test.go @@ -28,7 +28,10 @@ func TestHostCABundle(t *testing.T) { HostCABundlePath: "/etc/ssl/certs/ca-certificates.crt", } - hcli, err := helm.NewClient(helm.HelmOptions{}) + hcli, err := helm.NewClient(helm.HelmOptions{ + HelmPath: "helm", // use the helm binary in PATH + K8sVersion: "v1.33.0", + }) require.NoError(t, err, "NewClient should not return an error") err = addon.Install(context.Background(), t.Logf, nil, nil, hcli, ecv1beta1.Domains{}, nil) diff --git a/pkg/addons/openebs/static/metadata.yaml b/pkg/addons/openebs/static/metadata.yaml index 6064894f53..bbe86739e6 100644 --- a/pkg/addons/openebs/static/metadata.yaml +++ b/pkg/addons/openebs/static/metadata.yaml @@ -5,16 +5,21 @@ # $ make buildtools # $ output/bin/buildtools update addon # -version: 4.4.0 +version: 4.3.3 location: oci://proxy.replicated.com/anonymous/registry.replicated.com/ec-charts/openebs images: + kubectl: + repo: proxy.replicated.com/library/kubectl + tag: + amd64: 1.34.2-amd64@sha256:5383cd50f71e8971700afc69f67c91113a09f05905b04df1a61257854190f26c + arm64: 1.34.2-arm64@sha256:c976b98fa758b16152b8da0dab7e3d7e668aded6f0a859ec7afb36e8ee474827 openebs-linux-utils: repo: proxy.replicated.com/library/openebs-linux-utils tag: - amd64: 4.3.0-amd64@sha256:df790b30c5f472d71ca6102164bd7d2af0a21ccea4f61db42d6d70a29fb0e3cb - arm64: 4.3.0-arm64@sha256:948a417a826731ffeb83cde18f2610710425d674436531f04e658a8eba690d25 + amd64: 4.2.0-amd64@sha256:0e3c39e01c170593a4baab34ad3469e08e106f75e64203b2e87f9c2c55f9dae7 + arm64: 4.2.0-arm64@sha256:da72692224cb9fd7e0120297bd53f4bdeb45ec6280822846afd292aae1b89fe1 openebs-provisioner-localpv: repo: proxy.replicated.com/library/openebs-provisioner-localpv tag: - amd64: 4.4.0-amd64@sha256:a1e7eeeef83e48c86ce69e04855c301b93dab478aaf19e9cf8b4ae6433502c62 - arm64: 4.4.0-arm64@sha256:343f11c0b8708fa4ed3d60331fe3eaf1420871a51cac27cb7fedd0017ea49f7a + amd64: 4.3.0-amd64@sha256:7f3325a2249eba601fd222a23e27f741e9e6ddc5663ab9f5b8f7587930d1ef8d + arm64: 4.3.0-arm64@sha256:a218a9ad454a7452c663fa82453ec96b524faf220ccea8c7c2047ddc9c86fcfc diff --git a/pkg/addons/velero/integration/hostcabundle_test.go b/pkg/addons/velero/integration/hostcabundle_test.go index 3a0056472a..cf9a2d3a1b 100644 --- a/pkg/addons/velero/integration/hostcabundle_test.go +++ b/pkg/addons/velero/integration/hostcabundle_test.go @@ -22,7 +22,10 @@ func TestHostCABundle(t *testing.T) { HostCABundlePath: "/etc/ssl/certs/ca-certificates.crt", } - hcli, err := helm.NewClient(helm.HelmOptions{}) + hcli, err := helm.NewClient(helm.HelmOptions{ + HelmPath: "helm", // use the helm binary in PATH + K8sVersion: "v1.33.0", + }) require.NoError(t, err, "NewClient should not return an error") err = addon.Install(context.Background(), t.Logf, nil, nil, hcli, ecv1beta1.Domains{}, nil) diff --git a/pkg/addons/velero/integration/k0ssubdir_test.go b/pkg/addons/velero/integration/k0ssubdir_test.go index 90b78b9a38..e711b888db 100644 --- a/pkg/addons/velero/integration/k0ssubdir_test.go +++ b/pkg/addons/velero/integration/k0ssubdir_test.go @@ -24,7 +24,10 @@ func TestK0sDir(t *testing.T) { K0sDataDir: k0sDir, } - hcli, err := helm.NewClient(helm.HelmOptions{}) + hcli, err := helm.NewClient(helm.HelmOptions{ + HelmPath: "helm", // use the helm binary in PATH + K8sVersion: "v1.33.0", + }) require.NoError(t, err, "NewClient should not return an error") err = addon.Install(context.Background(), t.Logf, nil, nil, hcli, ecv1beta1.Domains{}, nil) diff --git a/pkg/addons/velero/static/metadata.yaml b/pkg/addons/velero/static/metadata.yaml index 113d252fea..0d6edd5d0b 100644 --- a/pkg/addons/velero/static/metadata.yaml +++ b/pkg/addons/velero/static/metadata.yaml @@ -11,8 +11,8 @@ images: kubectl: repo: proxy.replicated.com/library/kubectl tag: - amd64: 1.34.2-amd64@sha256:3ee82c4fbdcd324861aa437e65763f935bcd95b905f2dcf8bc7254d6f0f861d5 - arm64: 1.34.2-arm64@sha256:0524a9c1e29157bcbb62e5f065791371f495357be4e29a9e4c8f8dda400d90c6 + amd64: 1.34.2-amd64@sha256:5383cd50f71e8971700afc69f67c91113a09f05905b04df1a61257854190f26c + arm64: 1.34.2-arm64@sha256:c976b98fa758b16152b8da0dab7e3d7e668aded6f0a859ec7afb36e8ee474827 velero: repo: proxy.replicated.com/library/velero tag: diff --git a/pkg/helm/binary_executor.go b/pkg/helm/binary_executor.go index 3d39c3c8e1..5b90da1519 100644 --- a/pkg/helm/binary_executor.go +++ b/pkg/helm/binary_executor.go @@ -5,7 +5,6 @@ import ( "context" "io" "maps" - "path/filepath" "regexp" "strings" @@ -26,14 +25,8 @@ type binaryExecutor struct { } // newBinaryExecutor creates a new binaryExecutor with the specified binary path and optional default environment -func newBinaryExecutor(bin string, homeDir string) BinaryExecutor { - // Configure helm environment variables for tmpdir isolation - helmEnv := map[string]string{ - "HELM_CACHE_HOME": filepath.Join(homeDir, ".cache"), - "HELM_CONFIG_HOME": filepath.Join(homeDir, ".config"), - "HELM_DATA_HOME": filepath.Join(homeDir, ".local"), - } - return &binaryExecutor{bin: bin, defaultEnv: helmEnv} +func newBinaryExecutor(bin string, defaultEnv map[string]string) BinaryExecutor { + return &binaryExecutor{bin: bin, defaultEnv: defaultEnv} } // ExecuteCommand runs a command using helpers.RunCommandWithOptions and returns stdout, stderr, and error diff --git a/pkg/helm/binary_executor_test.go b/pkg/helm/binary_executor_test.go index 676c57cad4..dc0d0eaf33 100644 --- a/pkg/helm/binary_executor_test.go +++ b/pkg/helm/binary_executor_test.go @@ -32,7 +32,7 @@ func Test_binaryExecutor_ExecuteCommand(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - executor := newBinaryExecutor(tt.bin, t.TempDir()) + executor := newBinaryExecutor(tt.bin, nil) stdout, stderr, err := executor.ExecuteCommand(t.Context(), nil, nil, tt.args...) if tt.wantErr { @@ -95,7 +95,7 @@ func Test_binaryExecutor_ExecuteCommand_WithLogging(t *testing.T) { logs = append(logs, fmt.Sprintf(format, v...)) } - executor := newBinaryExecutor(tt.bin, t.TempDir()) + executor := newBinaryExecutor(tt.bin, nil) stdout, stderr, err := executor.ExecuteCommand(t.Context(), nil, logFn, tt.args...) if tt.wantErr { @@ -152,13 +152,18 @@ func Test_logWriter_Write(t *testing.T) { } func Test_binaryExecutor_EnvironmentMerging(t *testing.T) { - tmpDir := t.TempDir() - executor := newBinaryExecutor("sh", tmpDir) + // Test that default environment is merged with provided environment + defaultEnv := map[string]string{ + "DEFAULT_VAR": "default_value", + "OVERRIDE_ME": "default_override", + } + + executor := newBinaryExecutor("sh", defaultEnv) // Create a command that outputs all environment variables containing our test vars providedEnv := map[string]string{ - "PROVIDED_VAR": "provided_value", - "HELM_DATA_HOME": "overridden_value", // This should override the default + "PROVIDED_VAR": "provided_value", + "OVERRIDE_ME": "overridden_value", // This should override the default } // Use a shell command to check if our environment variables are set @@ -166,20 +171,19 @@ func Test_binaryExecutor_EnvironmentMerging(t *testing.T) { t.Context(), providedEnv, nil, - "-c", "echo HELM_CACHE_HOME=$HELM_CACHE_HOME HELM_CONFIG_HOME=$HELM_CONFIG_HOME HELM_DATA_HOME=$HELM_DATA_HOME PROVIDED_VAR=$PROVIDED_VAR", + "-c", "echo DEFAULT_VAR=$DEFAULT_VAR PROVIDED_VAR=$PROVIDED_VAR OVERRIDE_ME=$OVERRIDE_ME", ) require.NoError(t, err) // Verify that: // 1. Default env var is present - assert.Contains(t, stdout, "HELM_CACHE_HOME="+tmpDir) - assert.Contains(t, stdout, "HELM_CONFIG_HOME="+tmpDir) + assert.Contains(t, stdout, "DEFAULT_VAR=default_value") // 2. Provided env var is present assert.Contains(t, stdout, "PROVIDED_VAR=provided_value") // 3. Provided env var overrides default - assert.Contains(t, stdout, "HELM_DATA_HOME=overridden_value") - assert.NotContains(t, stdout, "HELM_DATA_HOME="+tmpDir) + assert.Contains(t, stdout, "OVERRIDE_ME=overridden_value") + assert.NotContains(t, stdout, "OVERRIDE_ME=default_override") } func Test_MockBinaryExecutor_ExecuteCommand(t *testing.T) { diff --git a/pkg/helm/client.go b/pkg/helm/client.go index a4a8b82966..8bd13e0659 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -1,12 +1,9 @@ package helm import ( - "bytes" "context" "encoding/json" - "errors" "fmt" - "io" "os" "path/filepath" "strings" @@ -15,50 +12,14 @@ import ( "github.com/Masterminds/semver/v3" "github.com/sirupsen/logrus" "github.com/spf13/pflag" - "go.yaml.in/yaml/v3" - "helm.sh/helm/v3/pkg/action" + "gopkg.in/yaml.v3" "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" helmcli "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/pusher" - "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/storage/driver" - "helm.sh/helm/v3/pkg/uploader" - "k8s.io/cli-runtime/pkg/genericclioptions" - restclient "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" k8syaml "sigs.k8s.io/yaml" ) -var ( - // getters is a list of known getters for both http and - // oci schemes. - getters = getter.Providers{ - getter.Provider{ - Schemes: []string{"http", "https"}, - New: getter.NewHTTPGetter, - }, - getter.Provider{ - Schemes: []string{"oci"}, - New: getter.NewOCIGetter, - }, - } - - // pushers holds all supported pushers (uploaders). - pushers = pusher.Providers{ - pusher.Provider{ - Schemes: []string{"oci"}, - New: pusher.NewOCIPusher, - }, - } -) - var _ Client = (*HelmClient)(nil) func newClient(opts HelmOptions) (*HelmClient, error) { @@ -76,22 +37,19 @@ func newClient(opts HelmOptions) (*HelmClient, error) { kversion = sv } - registryOpts := []registry.ClientOption{} - if opts.Writer != nil { - registryOpts = append(registryOpts, registry.ClientOptWriter(opts.Writer)) - } - regcli, err := registry.NewClient(registryOpts...) - if err != nil { - return nil, fmt.Errorf("create registry client: %w", err) + // Configure helm environment variables for tmpdir isolation + helmEnv := map[string]string{ + "HELM_CACHE_HOME": filepath.Join(tmpdir, ".cache"), + "HELM_CONFIG_HOME": filepath.Join(tmpdir, ".config"), + "HELM_DATA_HOME": filepath.Join(tmpdir, ".local"), } return &HelmClient{ helmPath: opts.HelmPath, - executor: newBinaryExecutor(opts.HelmPath, tmpdir), + executor: newBinaryExecutor(opts.HelmPath, helmEnv), tmpdir: tmpdir, kversion: kversion, kubernetesEnvSettings: opts.KubernetesEnvSettings, - regcli: regcli, airgapPath: opts.AirgapPath, repositories: []*repo.Entry{}, }, nil @@ -102,7 +60,6 @@ type HelmOptions struct { KubernetesEnvSettings *helmcli.EnvSettings K8sVersion string AirgapPath string - Writer io.Writer } type LogFn func(format string, args ...interface{}) @@ -153,45 +110,19 @@ type HelmClient struct { tmpdir string // Temporary directory for helm kversion *semver.Version // Kubernetes version for template rendering kubernetesEnvSettings *helmcli.EnvSettings // Kubernetes environment settings - regcli *registry.Client - repocfg string - repos []*repo.Entry - reposChanged bool - airgapPath string // Airgap path where charts are stored - repositories []*repo.Entry // Repository entries for helm repo commands + airgapPath string // Airgap path where charts are stored + repositories []*repo.Entry // Repository entries for helm repo commands } -func (h *HelmClient) prepare(_ context.Context) error { - // NOTE: this is a hack and should be refactored - if !h.reposChanged { - return nil - } - - data, err := k8syaml.Marshal(repo.File{Repositories: h.repos}) - if err != nil { - return fmt.Errorf("marshal repositories: %w", err) - } - - repocfg := filepath.Join(h.tmpdir, "config.yaml") - if err := os.WriteFile(repocfg, data, 0644); err != nil { - return fmt.Errorf("write repositories: %w", err) - } - - for _, repository := range h.repos { - chrepo, err := repo.NewChartRepository( - repository, getters, - ) - if err != nil { - return fmt.Errorf("create chart repo: %w", err) - } - chrepo.CachePath = h.tmpdir - _, err = chrepo.DownloadIndexFile() +func (h *HelmClient) prepare(ctx context.Context) error { + // Update all repositories to ensure we have the latest chart information + for _, repo := range h.repositories { + args := []string{"repo", "update", repo.Name} + _, _, err := h.executor.ExecuteCommand(ctx, nil, nil, args...) if err != nil { - return fmt.Errorf("download index file: %w", err) + return fmt.Errorf("helm repo update %s: %w", repo.Name, err) } } - h.repocfg = repocfg - h.reposChanged = false return nil } @@ -199,15 +130,7 @@ func (h *HelmClient) Close() error { return os.RemoveAll(h.tmpdir) } -func (h *HelmClient) AddRepo(_ context.Context, repo *repo.Entry) error { - h.repos = append(h.repos, repo) - h.reposChanged = true - return nil -} - -// AddRepoBin adds a repository to the helm client using the helm binary. This is necessary because -// the AddRepo method does not work with other methods using the binary executor. -func (h *HelmClient) AddRepoBin(ctx context.Context, repo *repo.Entry) error { +func (h *HelmClient) AddRepo(ctx context.Context, repo *repo.Entry) error { // Use helm repo add command to add the repository args := []string{"repo", "add", repo.Name, repo.URL} @@ -288,40 +211,62 @@ func (h *HelmClient) Pull(ctx context.Context, reponame, chart string, version s } func (h *HelmClient) PullByRef(ctx context.Context, ref string, version string) (string, error) { + // Update repositories if this is not an OCI chart if !isOCIChart(ref) { if err := h.prepare(ctx); err != nil { return "", fmt.Errorf("prepare: %w", err) } } - dl := downloader.ChartDownloader{ - Out: io.Discard, - Options: []getter.Option{}, - RepositoryConfig: h.repocfg, - RepositoryCache: h.tmpdir, - Getters: getters, + // Use helm pull to download the chart + args := []string{"pull", ref} + if version != "" { + args = append(args, "--version", version) } + args = append(args, "--destination", h.tmpdir) + + // Add debug flag to report progress and capture debug logs + args = append(args, "--debug") - dst, _, err := dl.DownloadTo(ref, version, os.TempDir()) + _, _, err := h.executor.ExecuteCommand(ctx, nil, nil, args...) if err != nil { - return "", fmt.Errorf("download chart %s: %w", ref, err) + return "", fmt.Errorf("helm pull: %w", err) } - return dst, nil + // Get chart metadata to determine the actual chart name and construct filename + metadata, err := h.GetChartMetadata(ctx, ref, version) + if err != nil { + return "", fmt.Errorf("get chart metadata: %w", err) + } + + // Construct expected filename (chart name + version + .tgz) + chartPath := filepath.Join(h.tmpdir, fmt.Sprintf("%s-%s.tgz", metadata.Name, metadata.Version)) + + return chartPath, nil } -func (h *HelmClient) RegistryAuth(_ context.Context, server, user, pass string) error { - return h.regcli.Login(server, registry.LoginOptBasicAuth(user, pass)) +func (h *HelmClient) RegistryAuth(ctx context.Context, server, user, pass string) error { + // Use helm registry login for authentication + args := []string{"registry", "login", server, "--username", user, "--password", pass} + + _, _, err := h.executor.ExecuteCommand(ctx, nil, nil, args...) + if err != nil { + return fmt.Errorf("helm registry login: %w", err) + } + + return nil } -func (h *HelmClient) Push(_ context.Context, path, dst string) error { - up := uploader.ChartUploader{ - Out: os.Stdout, - Pushers: pushers, - Options: []pusher.Option{pusher.WithRegistryClient(h.regcli)}, +func (h *HelmClient) Push(ctx context.Context, path, dst string) error { + // Use helm push to upload the chart + args := []string{"push", path, dst} + + _, _, err := h.executor.ExecuteCommand(ctx, nil, nil, args...) + if err != nil { + return fmt.Errorf("helm push: %w", err) } - return up.UploadTo(path, dst) + return nil } func (h *HelmClient) GetChartMetadata(ctx context.Context, ref string, version string) (*chart.Metadata, error) { @@ -343,310 +288,419 @@ func (h *HelmClient) GetChartMetadata(ctx context.Context, ref string, version s return &metadata, nil } -// reference: https://github.com/helm/helm/blob/0d66425d9a745d8a289b1a5ebb6ccc744436da95/cmd/helm/upgrade.go#L122-L125 -func (h *HelmClient) ReleaseExists(ctx context.Context, namespace string, releaseName string) (bool, error) { - cfg, err := h.getActionCfg(namespace, nil) - if err != nil { - return false, fmt.Errorf("get action configuration: %w", err) - } +// ReleaseHistoryEntry represents a single entry in helm release history +type ReleaseHistoryEntry struct { + Revision int `json:"revision"` + Status release.Status `json:"status"` +} - client := action.NewHistory(cfg) - client.Max = 1 +// ReleaseHistory returns the release history for a given release +func (h *HelmClient) ReleaseHistory(ctx context.Context, namespace string, releaseName string, maxRevisions int) ([]ReleaseHistoryEntry, error) { + args := []string{"history", releaseName, "--namespace", namespace, "--output", "json"} - versions, err := client.Run(releaseName) - if errors.Is(err, driver.ErrReleaseNotFound) || isReleaseUninstalled(versions) { - return false, nil + if maxRevisions > 0 { + args = append(args, "--max", fmt.Sprintf("%d", maxRevisions)) } + + // Add kubeconfig and context if available + args = h.addKubernetesEnvArgs(args) + + stdout, _, err := h.executor.ExecuteCommand(ctx, nil, nil, args...) if err != nil { - return false, fmt.Errorf("get release history: %w", err) + return nil, fmt.Errorf("helm history: %w", err) } - return true, nil -} + var history []ReleaseHistoryEntry + if err := json.Unmarshal([]byte(stdout), &history); err != nil { + return nil, fmt.Errorf("parse release history json: %w", err) + } -func isReleaseUninstalled(versions []*release.Release) bool { - return len(versions) > 0 && versions[len(versions)-1].Info.Status == release.StatusUninstalled + return history, nil } -func (h *HelmClient) Install(ctx context.Context, opts InstallOptions) (*release.Release, error) { - cfg, err := h.getActionCfg(opts.Namespace, opts.LogFn) +// GetLastRevision returns the revision number of the latest release entry +func (h *HelmClient) GetLastRevision(ctx context.Context, namespace string, releaseName string) (int, error) { + history, err := h.ReleaseHistory(ctx, namespace, releaseName, 1) if err != nil { - return nil, fmt.Errorf("get action configuration: %w", err) + return 0, fmt.Errorf("get release history: %w", err) } - client := action.NewInstall(cfg) - client.ReleaseName = opts.ReleaseName - client.Namespace = opts.Namespace - client.Labels = opts.Labels - client.Replace = true - client.CreateNamespace = true - client.WaitForJobs = true - client.Wait = true - // we don't set client.Atomic = true on install as it makes installation failures difficult to - // debug since it will rollback the release. - - if opts.Timeout != 0 { - client.Timeout = opts.Timeout - } else { - client.Timeout = 5 * time.Minute + if len(history) == 0 { + return 0, fmt.Errorf("no release history found for %s", releaseName) } - chartRequested, err := h.loadChart(ctx, opts.ReleaseName, opts.ChartPath, opts.ChartVersion) + return history[0].Revision, nil +} + +func (h *HelmClient) ReleaseExists(ctx context.Context, namespace string, releaseName string) (bool, error) { + history, err := h.ReleaseHistory(ctx, namespace, releaseName, 1) if err != nil { - return nil, fmt.Errorf("load chart: %w", err) + if strings.Contains(err.Error(), "release: not found") { + return false, nil + } + return false, fmt.Errorf("get release history: %w", err) } - if req := chartRequested.Metadata.Dependencies; req != nil { - if err := action.CheckDependencies(chartRequested, req); err != nil { - return nil, fmt.Errorf("check chart dependencies: %w", err) - } + // True if release has history and is not uninstalled + exists := len(history) > 0 && history[0].Status != release.StatusUninstalled + + return exists, nil +} + +// createValuesFile creates a temporary values file from the provided values map +func (h *HelmClient) createValuesFile(values map[string]interface{}) (string, error) { + if h.tmpdir == "" { + return "", fmt.Errorf("tmpdir not initialized") } - cleanVals, err := cleanUpGenericMap(opts.Values) + cleanVals, err := cleanUpGenericMap(values) if err != nil { - return nil, fmt.Errorf("clean up generic map: %w", err) + return "", fmt.Errorf("clean up generic map: %w", err) } - release, err := client.RunWithContext(ctx, chartRequested, cleanVals) + data, err := k8syaml.Marshal(cleanVals) if err != nil { - return nil, fmt.Errorf("helm install: %w", err) + return "", fmt.Errorf("marshal values: %w", err) + } + + // Use unique filename to prevent race conditions + valuesFile := filepath.Join(h.tmpdir, fmt.Sprintf("values-%d.yaml", time.Now().UnixNano())) + if err := os.WriteFile(valuesFile, data, 0644); err != nil { + return "", fmt.Errorf("write values file: %w", err) } - return release, nil + return valuesFile, nil } -func (h *HelmClient) Upgrade(ctx context.Context, opts UpgradeOptions) (*release.Release, error) { - cfg, err := h.getActionCfg(opts.Namespace, opts.LogFn) +func (h *HelmClient) Install(ctx context.Context, opts InstallOptions) (string, error) { + // Build helm install command arguments + args := []string{"install", opts.ReleaseName} + + // Handle chart source + chartPath, err := h.resolveChartPath(ctx, opts.ReleaseName, opts.ChartPath, opts.ChartVersion) if err != nil { - return nil, fmt.Errorf("get action configuration: %w", err) + return "", fmt.Errorf("resolve chart path: %w", err) } + args = append(args, chartPath) - client := action.NewUpgrade(cfg) - client.Namespace = opts.Namespace - client.Labels = opts.Labels - client.WaitForJobs = true - client.Wait = true - client.Atomic = true - client.Force = opts.Force - - if opts.Timeout != 0 { - client.Timeout = opts.Timeout - } else { - client.Timeout = 5 * time.Minute + // Add namespace + if opts.Namespace != "" { + args = append(args, "--namespace", opts.Namespace) + args = append(args, "--create-namespace") } - chartRequested, err := h.loadChart(ctx, opts.ReleaseName, opts.ChartPath, opts.ChartVersion) - if err != nil { - return nil, fmt.Errorf("load chart: %w", err) + // Add wait options + args = append(args, "--wait") + args = append(args, "--wait-for-jobs") + + // Add timeout + timeout := opts.Timeout + if timeout == 0 { + timeout = 5 * time.Minute } + args = append(args, "--timeout", timeout.String()) + + // Add replace flag + args = append(args, "--replace") - if req := chartRequested.Metadata.Dependencies; req != nil { - if err := action.CheckDependencies(chartRequested, req); err != nil { - return nil, fmt.Errorf("check chart dependencies: %w", err) + // Add values if provided + if opts.Values != nil { + valuesFile, err := h.createValuesFile(opts.Values) + if err != nil { + return "", fmt.Errorf("create values file: %w", err) } + defer os.Remove(valuesFile) + args = append(args, "--values", valuesFile) } - cleanVals, err := cleanUpGenericMap(opts.Values) - if err != nil { - return nil, fmt.Errorf("clean up generic map: %w", err) + // Add labels if provided + if opts.Labels != nil { + var labelPairs []string + for k, v := range opts.Labels { + labelPairs = append(labelPairs, fmt.Sprintf("%s=%s", k, v)) + } + args = append(args, "--labels", strings.Join(labelPairs, ",")) } - release, err := client.RunWithContext(ctx, opts.ReleaseName, chartRequested, cleanVals) + // Add kubeconfig and context if available + args = h.addKubernetesEnvArgs(args) + + // Add debug flag to report progress and capture debug logs + args = append(args, "--debug") + + // NOTE: we don't set client.Atomic = true on install as it makes installation failures difficult to debug + // since it will rollback the release. + + // Execute helm install command + stdout, _, err := h.executor.ExecuteCommand(ctx, nil, opts.LogFn, args...) if err != nil { - return nil, fmt.Errorf("helm upgrade: %w", err) + return "", fmt.Errorf("execute: %w", err) } - return release, nil + return stdout, nil } -func (h *HelmClient) Uninstall(ctx context.Context, opts UninstallOptions) error { - cfg, err := h.getActionCfg(opts.Namespace, opts.LogFn) - if err != nil { - return fmt.Errorf("get action configuration: %w", err) +// resolveChartPath handles chart source resolution for install, upgrade, and render operations +func (h *HelmClient) resolveChartPath(ctx context.Context, releaseName, chartPath, chartVersion string) (string, error) { + if h.airgapPath != "" { + // Use chart from airgap path + return filepath.Join(h.airgapPath, fmt.Sprintf("%s-%s.tgz", releaseName, chartVersion)), nil } + if !strings.HasPrefix(chartPath, "/") { + // Pull chart with retries (includes oci:// prefix) + localPath, err := h.PullByRefWithRetries(ctx, chartPath, chartVersion, 3) + if err != nil { + return "", fmt.Errorf("pull chart: %w", err) + } + if localPath == "" { + return "", fmt.Errorf("pulled chart path is empty") + } + return localPath, nil + } + // Use local chart path + return chartPath, nil +} - client := action.NewUninstall(cfg) - client.Wait = opts.Wait - client.IgnoreNotFound = opts.IgnoreNotFound +func (h *HelmClient) Upgrade(ctx context.Context, opts UpgradeOptions) (string, error) { + // Build helm upgrade command arguments + args := []string{"upgrade", opts.ReleaseName} - if deadline, ok := ctx.Deadline(); ok { - client.Timeout = time.Until(deadline) + // Handle chart source + chartPath, err := h.resolveChartPath(ctx, opts.ReleaseName, opts.ChartPath, opts.ChartVersion) + if err != nil { + return "", fmt.Errorf("resolve chart path: %w", err) } + args = append(args, chartPath) - if _, err := client.Run(opts.ReleaseName); err != nil { - return fmt.Errorf("uninstall release: %w", err) + // Add namespace + if opts.Namespace != "" { + args = append(args, "--namespace", opts.Namespace) } - return nil -} + // Add wait options + args = append(args, "--wait") + args = append(args, "--wait-for-jobs") -func (h *HelmClient) Render(ctx context.Context, opts InstallOptions) ([][]byte, error) { - cfg := &action.Configuration{} - - client := action.NewInstall(cfg) - client.DryRun = true - client.ReleaseName = opts.ReleaseName - client.Replace = true - client.CreateNamespace = true - client.ClientOnly = true - client.IncludeCRDs = true - client.Namespace = opts.Namespace - client.Labels = opts.Labels + // Add timeout + timeout := opts.Timeout + if timeout == 0 { + timeout = 5 * time.Minute + } + args = append(args, "--timeout", timeout.String()) - if h.kversion != nil { - // since ClientOnly is true we need to initialize KubeVersion otherwise resorts defaults - client.KubeVersion = &chartutil.KubeVersion{ - Version: fmt.Sprintf("v%d.%d.0", h.kversion.Major(), h.kversion.Minor()), - Major: fmt.Sprintf("%d", h.kversion.Major()), - Minor: fmt.Sprintf("%d", h.kversion.Minor()), - } + // Add atomic flag + args = append(args, "--atomic") + + // Add force flag if specified + if opts.Force { + args = append(args, "--force") } - chartRequested, err := h.loadChart(ctx, opts.ReleaseName, opts.ChartPath, opts.ChartVersion) - if err != nil { - return nil, fmt.Errorf("load chart: %w", err) + // Add values if provided + if opts.Values != nil { + valuesFile, err := h.createValuesFile(opts.Values) + if err != nil { + return "", fmt.Errorf("create values file: %w", err) + } + defer os.Remove(valuesFile) + args = append(args, "--values", valuesFile) } - if req := chartRequested.Metadata.Dependencies; req != nil { - if err := action.CheckDependencies(chartRequested, req); err != nil { - return nil, fmt.Errorf("failed dependency check: %w", err) + // Add labels if provided + if opts.Labels != nil { + var labelPairs []string + for k, v := range opts.Labels { + labelPairs = append(labelPairs, fmt.Sprintf("%s=%s", k, v)) } + args = append(args, "--labels", strings.Join(labelPairs, ",")) } - cleanVals, err := cleanUpGenericMap(opts.Values) + // Add kubernetes environment arguments + args = h.addKubernetesEnvArgs(args) + + // Add debug flag to report progress and capture debug logs + args = append(args, "--debug") + + // Execute helm upgrade command + stdout, stderr, err := h.executor.ExecuteCommand(ctx, nil, opts.LogFn, args...) if err != nil { - return nil, fmt.Errorf("clean up generic map: %w", err) + if shouldRollback(err.Error()) || shouldRollback(stderr) { + // Get the last revision + lastRevision, err := h.GetLastRevision(ctx, opts.Namespace, opts.ReleaseName) + if err != nil { + return "", fmt.Errorf("get last revision: %w", err) + } + + // Rollback to the latest revision + if _, err := h.Rollback(ctx, RollbackOptions{ + ReleaseName: opts.ReleaseName, + Namespace: opts.Namespace, + Revision: lastRevision, + Timeout: opts.Timeout, + Force: opts.Force, + LogFn: opts.LogFn, + }); err != nil { + return "", fmt.Errorf("rollback: %w", err) + } + + // Retry upgrade after successful rollback + return h.Upgrade(ctx, opts) + } + + return "", fmt.Errorf("helm upgrade failed: %w", err) } - release, err := client.Run(chartRequested, cleanVals) - if err != nil { - return nil, fmt.Errorf("run render: %w", err) + return stdout, nil +} + +func shouldRollback(err string) bool { + return strings.Contains(err, "another operation") && strings.Contains(err, "in progress") +} + +func (h *HelmClient) Rollback(ctx context.Context, opts RollbackOptions) (string, error) { + args := []string{"rollback", opts.ReleaseName} + + // If specific revision is provided, use it + if opts.Revision > 0 { + args = append(args, fmt.Sprintf("%d", opts.Revision)) } - var manifests bytes.Buffer - fmt.Fprintln(&manifests, strings.TrimSpace(release.Manifest)) - for _, m := range release.Hooks { - fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest) + // Add namespace + if opts.Namespace != "" { + args = append(args, "--namespace", opts.Namespace) } - splitManifests, err := splitManifests(manifests.String()) - if err != nil { - return nil, fmt.Errorf("split manifests: %w", err) + // Add wait options + args = append(args, "--wait") + args = append(args, "--wait-for-jobs") + + // Add timeout + timeout := opts.Timeout + if timeout == 0 { + timeout = 5 * time.Minute } - return splitManifests, nil -} + args = append(args, "--timeout", timeout.String()) -func (h *HelmClient) getActionCfg(namespace string, logFn LogFn) (*action.Configuration, error) { - cfg := &action.Configuration{} - if logFn == nil { - logFn = _logFn + // Add force flag if specified + if opts.Force { + args = append(args, "--force") } - var restClientGetter genericclioptions.RESTClientGetter - if h.kubernetesEnvSettings != nil { - restClientGetter = h.kubernetesEnvSettings.RESTClientGetter() - } else { - restClientGetter = helmcli.New().RESTClientGetter() // use the default env settings from helm + + // Add kubernetes environment arguments + args = h.addKubernetesEnvArgs(args) + + // Add debug flag to report progress and capture debug logs + args = append(args, "--debug") + + stdout, _, err := h.executor.ExecuteCommand(ctx, nil, opts.LogFn, args...) + if err != nil { + return "", fmt.Errorf("execute: %w", err) } - restClientGetter = &namespacedRESTClientGetter{ - RESTClientGetter: restClientGetter, - namespace: namespace, + + return stdout, nil +} + +func (h *HelmClient) Uninstall(ctx context.Context, opts UninstallOptions) error { + // Build helm uninstall command arguments + args := []string{"uninstall", opts.ReleaseName} + + // Add namespace + if opts.Namespace != "" { + args = append(args, "--namespace", opts.Namespace) } - if err := cfg.Init(restClientGetter, namespace, "secret", action.DebugLog(logFn)); err != nil { - return nil, fmt.Errorf("init helm configuration: %w", err) + + // Add wait flag + if opts.Wait { + args = append(args, "--wait") } - return cfg, nil -} -func (h *HelmClient) loadChart(ctx context.Context, releaseName, chartPath, chartVersion string) (*chart.Chart, error) { - var localPath string - if _, err := os.Stat(chartPath); err == nil { - localPath = chartPath - } else if h.airgapPath != "" { - // airgapped, use chart from airgap path - // TODO: this should just respect the chart path if it's a local path and leave it up to the caller to handle - localPath = filepath.Join(h.airgapPath, fmt.Sprintf("%s-%s.tgz", releaseName, chartVersion)) - } else if !strings.HasPrefix(chartPath, "/") { - // Assume this is a chart from a repo if it doesn't start with a / - // This includes oci:// prefix - var err error - localPath, err = h.PullByRefWithRetries(ctx, chartPath, chartVersion, 3) - if err != nil { - return nil, fmt.Errorf("pull: %w", err) - } - defer os.RemoveAll(localPath) + // Add ignore not found flag + if opts.IgnoreNotFound { + args = append(args, "--ignore-not-found") } - if localPath == "" { - return nil, fmt.Errorf("chart path not found: %s", chartPath) + // Add kubeconfig and context if available + args = h.addKubernetesEnvArgs(args) + + // Add debug flag to report progress and capture debug logs + args = append(args, "--debug") + + // Add timeout from context if available + if deadline, ok := ctx.Deadline(); ok { + timeout := time.Until(deadline) + args = append(args, "--timeout", timeout.String()) } - chartRequested, err := loader.Load(localPath) + // Execute helm uninstall command + _, _, err := h.executor.ExecuteCommand(ctx, nil, opts.LogFn, args...) if err != nil { - return nil, fmt.Errorf("load: %w", err) + return fmt.Errorf("execute: %w", err) } - return chartRequested, nil + return nil } -func cleanUpGenericMap(m map[string]interface{}) (map[string]interface{}, error) { - // we must first use yaml marshal to convert the map[interface{}]interface{} to a []byte - // otherwise we will get an error "unsupported type: map[interface {}]interface {}" - b, err := yaml.Marshal(m) +func (h *HelmClient) Render(ctx context.Context, opts InstallOptions) ([][]byte, error) { + // Build helm template command arguments + args := []string{"template", opts.ReleaseName} + + // Handle chart source + chartPath, err := h.resolveChartPath(ctx, opts.ReleaseName, opts.ChartPath, opts.ChartVersion) if err != nil { - return nil, fmt.Errorf("yaml marshal: %w", err) + return nil, fmt.Errorf("resolve chart path: %w", err) } - next := map[string]interface{}{} - err = k8syaml.Unmarshal(b, &next) - if err != nil { - return nil, fmt.Errorf("yaml unmarshal: %w", err) - } - return next, nil -} + args = append(args, chartPath) -func isOCIChart(chartPath string) bool { - return strings.HasPrefix(chartPath, "oci://") -} + // Add namespace + if opts.Namespace != "" { + args = append(args, "--namespace", opts.Namespace) + args = append(args, "--create-namespace") + } -func _logFn(format string, args ...interface{}) { - log := logrus.WithField("component", "helm") - log.Debugf(format, args...) -} + // Add labels if provided + if opts.Labels != nil { + var labelPairs []string + for k, v := range opts.Labels { + labelPairs = append(labelPairs, fmt.Sprintf("%s=%s", k, v)) + } + args = append(args, "--labels", strings.Join(labelPairs, ",")) + } -type namespacedRESTClientGetter struct { - genericclioptions.RESTClientGetter - namespace string -} + // Add values if provided + if opts.Values != nil { + valuesFile, err := h.createValuesFile(opts.Values) + if err != nil { + return nil, fmt.Errorf("create values file: %w", err) + } + defer os.Remove(valuesFile) + args = append(args, "--values", valuesFile) + } -func (n *namespacedRESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig { - cfg := n.RESTClientGetter.ToRawKubeConfigLoader() - return &namespacedClientConfig{ - cfg: cfg, - namespace: n.namespace, + // Add kubernetes version if available + if h.kversion != nil { + args = append(args, "--kube-version", fmt.Sprintf("v%d.%d.0", h.kversion.Major(), h.kversion.Minor())) } -} -type namespacedClientConfig struct { - cfg clientcmd.ClientConfig - namespace string -} + // Add kubeconfig and context if available + args = h.addKubernetesEnvArgs(args) -func (n *namespacedClientConfig) RawConfig() (clientcmdapi.Config, error) { - return n.cfg.RawConfig() -} + // Add include CRDs flag + args = append(args, "--include-crds") -func (n *namespacedClientConfig) ClientConfig() (*restclient.Config, error) { - return n.cfg.ClientConfig() -} + // Add debug flag to report progress and capture debug logs + args = append(args, "--debug") -func (n *namespacedClientConfig) Namespace() (string, bool, error) { - if n.namespace == "" { - return n.cfg.Namespace() + // Execute helm template command + stdout, _, err := h.executor.ExecuteCommand(ctx, nil, opts.LogFn, args...) + if err != nil { + return nil, fmt.Errorf("execute: %w", err) } - return n.namespace, true, nil -} -func (n *namespacedClientConfig) ConfigAccess() clientcmd.ConfigAccess { - return n.cfg.ConfigAccess() + manifests, err := splitManifests(stdout) + if err != nil { + return nil, fmt.Errorf("parse helm template output: %w", err) + } + return manifests, nil } // addKubernetesEnvArgs adds kubernetes environment arguments to the helm command @@ -716,3 +770,22 @@ func AddKubernetesCLIFlags(flagSet *pflag.FlagSet, kubernetesEnvSettings *helmcl flagSet.IntVar(&kubernetesEnvSettings.BurstLimit, "burst-limit", kubernetesEnvSettings.BurstLimit, "Kubernetes API client-side default throttling limit") flagSet.Float32Var(&kubernetesEnvSettings.QPS, "qps", kubernetesEnvSettings.QPS, "Queries per second used when communicating with the Kubernetes API, not including bursting") } + +func cleanUpGenericMap(m map[string]interface{}) (map[string]interface{}, error) { + // we must first use yaml marshal to convert the map[interface{}]interface{} to a []byte + // otherwise we will get an error "unsupported type: map[interface {}]interface {}" + b, err := yaml.Marshal(m) + if err != nil { + return nil, fmt.Errorf("yaml marshal: %w", err) + } + next := map[string]interface{}{} + err = k8syaml.Unmarshal(b, &next) + if err != nil { + return nil, fmt.Errorf("yaml unmarshal: %w", err) + } + return next, nil +} + +func isOCIChart(chartPath string) bool { + return strings.HasPrefix(chartPath, "oci://") +} diff --git a/pkg/helm/client_test.go b/pkg/helm/client_test.go index e30266d717..7b87e2c2ff 100644 --- a/pkg/helm/client_test.go +++ b/pkg/helm/client_test.go @@ -1,78 +1,254 @@ package helm import ( + "fmt" + "os" + "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + helmcli "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/repo" k8syaml "sigs.k8s.io/yaml" ) -func TestHelmClient_Latest(t *testing.T) { +func TestHelmClient_PullByRef(t *testing.T) { tests := []struct { - name string - reponame string - chart string - setupMock func(*MockBinaryExecutor) - want string - wantErr bool + name string + ref string + version string + repositories []*repo.Entry + setupMock func(*MockBinaryExecutor) + want string + wantErr bool }{ { - name: "valid JSON response", - reponame: "myrepo", - chart: "mychart", + name: "successful pull with repository preparation", + ref: "myrepo/mychart", + version: "1.2.3", + repositories: []*repo.Entry{ + { + Name: "myrepo", + URL: "https://charts.example.com/myrepo", + }, + }, setupMock: func(m *MockBinaryExecutor) { - jsonOutput := `[ - { - "name": "myrepo/mychart", - "version": "1.2.3", - "app_version": "1.2.3", - "description": "A test chart" - } - ]` - m.On("ExecuteCommand", mock.Anything, mock.Anything, mock.Anything, - []string{"search", "repo", "myrepo/mychart", "--version", ">0.0.0", "--versions", "--output", "json"}). - Return(jsonOutput, "", nil) + // Mock helm repo update command (called by prepare()) + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"repo", "update", "myrepo"}, + ).Return("", "", nil) + + // Mock helm pull command + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + return len(args) == 7 && + args[0] == "pull" && + args[1] == "myrepo/mychart" && + args[2] == "--version" && + args[3] == "1.2.3" && + args[4] == "--destination" && + // args[5] is the temp directory path, which varies + args[6] == "--debug" + }), + ).Return("", "", nil) + + // Mock helm show chart command for metadata + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"show", "chart", "myrepo/mychart", "--version", "1.2.3"}, + ).Return(`apiVersion: v2 +name: mychart +description: A test chart from repo +type: application +version: 1.2.3 +appVersion: "1.0.0"`, "", nil) }, - want: "1.2.3", + want: "mychart-1.2.3.tgz", wantErr: false, }, { - name: "empty results", - reponame: "myrepo", - chart: "nonexistent", + name: "successful pull from OCI registry", + ref: "oci://registry.example.com/charts/nginx", + version: "2.1.0", + repositories: nil, // OCI charts don't use repositories setupMock: func(m *MockBinaryExecutor) { - m.On("ExecuteCommand", mock.Anything, mock.Anything, mock.Anything, - []string{"search", "repo", "myrepo/nonexistent", "--version", ">0.0.0", "--versions", "--output", "json"}). - Return("[]", "", nil) + // No helm repo update for OCI charts (prepare() is skipped) + + // Mock helm pull command for OCI + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + return len(args) == 7 && + args[0] == "pull" && + args[1] == "oci://registry.example.com/charts/nginx" && + args[2] == "--version" && + args[3] == "2.1.0" && + args[4] == "--destination" && + // args[5] is the temp directory path, which varies + args[6] == "--debug" + }), + ).Return("", "", nil) + + // Mock helm show chart command for metadata + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"show", "chart", "oci://registry.example.com/charts/nginx", "--version", "2.1.0"}, + ).Return(`apiVersion: v2 +name: nginx +description: A nginx chart from OCI registry +type: application +version: 2.1.0 +appVersion: "1.25.0"`, "", nil) }, - want: "", - wantErr: true, + want: "nginx-2.1.0.tgz", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + // Create temporary directory for the test + tmpdir := t.TempDir() + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + tmpdir: tmpdir, + repositories: tt.repositories, + } + + got, err := client.PullByRef(t.Context(), tt.ref, tt.version) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + // Check that the returned path ends with the expected filename + assert.True(t, strings.HasSuffix(got, tt.want)) + mockExec.AssertExpectations(t) + }) + } +} + +func TestHelmClient_Install(t *testing.T) { + tests := []struct { + name string + setupMock func(*MockBinaryExecutor) + kubernetesEnvSettings *helmcli.EnvSettings + opts InstallOptions + wantErr bool + }{ + { + name: "successful install", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"install", "myrelease", "/path/to/chart", "--namespace", "default", "--create-namespace", "--wait", "--wait-for-jobs", "--timeout", "5m0s", "--replace", "--debug"}, + ).Return(`Release "myrelease" has been upgraded.`, "", nil) + }, + kubernetesEnvSettings: nil, // No kubeconfig settings + opts: InstallOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + Timeout: 5 * time.Minute, + }, + wantErr: false, }, { - name: "helm command fails", - reponame: "myrepo", - chart: "mychart", + name: "install with values", setupMock: func(m *MockBinaryExecutor) { - m.On("ExecuteCommand", mock.Anything, mock.Anything, mock.Anything, - []string{"search", "repo", "myrepo/mychart", "--version", ">0.0.0", "--versions", "--output", "json"}). - Return("", "repo not found", assert.AnError) + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "install") && + strings.Contains(argsStr, "--values") + }), + ).Return(`Release "myrelease" has been installed.`, "", nil) }, - want: "", - wantErr: true, + kubernetesEnvSettings: nil, // No kubeconfig settings + opts: InstallOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + Timeout: 5 * time.Minute, + Values: map[string]interface{}{ + "key": "value", + }, + }, + wantErr: false, }, + { - name: "invalid JSON response", - reponame: "myrepo", - chart: "mychart", + name: "install with kubernetes env settings", setupMock: func(m *MockBinaryExecutor) { - m.On("ExecuteCommand", mock.Anything, mock.Anything, mock.Anything, - []string{"search", "repo", "myrepo/mychart", "--version", ">0.0.0", "--versions", "--output", "json"}). - Return("invalid json", "", nil) + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "install") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") && + strings.Contains(argsStr, "--kube-context test-context") && + strings.Contains(argsStr, "--kube-token test-token") && + strings.Contains(argsStr, "--kube-as-user test-user") && + strings.Contains(argsStr, "--kube-as-group test-group1") && + strings.Contains(argsStr, "--kube-as-group test-group2") && + strings.Contains(argsStr, "--kube-apiserver https://test-server:6443") && + strings.Contains(argsStr, "--kube-ca-file /tmp/ca.crt") && + strings.Contains(argsStr, "--kube-tls-server-name test-server") && + strings.Contains(argsStr, "--kube-insecure-skip-tls-verify") && + strings.Contains(argsStr, "--burst-limit 100") && + strings.Contains(argsStr, "--qps 50.00") + }), + ).Return(`Release "myrelease" has been installed.`, "", nil) }, - want: "", - wantErr: true, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + KubeContext: "test-context", + KubeToken: "test-token", + KubeAsUser: "test-user", + KubeAsGroups: []string{"test-group1", "test-group2"}, + KubeAPIServer: "https://test-server:6443", + KubeCaFile: "/tmp/ca.crt", + KubeTLSServerName: "test-server", + KubeInsecureSkipTLSVerify: true, + BurstLimit: 100, + QPS: 50.0, + }, + opts: InstallOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + Timeout: 5 * time.Minute, + }, + wantErr: false, }, } @@ -81,19 +257,267 @@ func TestHelmClient_Latest(t *testing.T) { mockExec := &MockBinaryExecutor{} tt.setupMock(mockExec) + // Create temporary directory for the test + tmpdir, err := os.MkdirTemp("", "helm-test-*") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + client := &HelmClient{ - helmPath: "/usr/local/bin/helm", - executor: mockExec, + helmPath: "/usr/local/bin/helm", + executor: mockExec, + tmpdir: tmpdir, + kubernetesEnvSettings: tt.kubernetesEnvSettings, } - got, err := client.Latest(t.Context(), tt.reponame, tt.chart) + stdout, err := client.Install(t.Context(), tt.opts) + if tt.wantErr { assert.Error(t, err) return } require.NoError(t, err) - assert.Equal(t, tt.want, got) + assert.NotEmpty(t, stdout) + mockExec.AssertExpectations(t) + }) + } +} + +func TestHelmClient_ReleaseExists(t *testing.T) { + tests := []struct { + name string + setupMock func(*MockBinaryExecutor) + kubernetesEnvSettings *helmcli.EnvSettings + namespace string + releaseName string + want bool + wantErr bool + }{ + { + name: "release exists", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "history") && + strings.Contains(argsStr, "myrelease") && + strings.Contains(argsStr, "--namespace default") && + strings.Contains(argsStr, "--max 1") && + strings.Contains(argsStr, "--output json") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") + }), + ).Return(`[{ + "revision": 1, + "updated": "2023-01-01T00:00:00Z", + "status": "deployed", + "chart": "test-chart-1.0.0", + "app_version": "1.0.0", + "description": "Install complete" + }]`, "", nil) + }, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + }, + namespace: "default", + releaseName: "myrelease", + want: true, + wantErr: false, + }, + { + name: "release does not exist", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"history", "myrelease", "--namespace", "default", "--output", "json", "--max", "1"}, + ).Return(`[]`, "", nil) + }, + kubernetesEnvSettings: nil, // No kubeconfig settings + namespace: "default", + releaseName: "myrelease", + want: false, + wantErr: false, + }, + { + name: "release exists but is uninstalled", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "history") && + strings.Contains(argsStr, "myrelease") && + strings.Contains(argsStr, "--namespace default") && + strings.Contains(argsStr, "--max 1") && + strings.Contains(argsStr, "--output json") + }), + ).Return(`[{ + "revision": 2, + "updated": "2023-01-01T01:00:00Z", + "status": "uninstalled", + "chart": "test-chart-1.0.0", + "app_version": "1.0.0", + "description": "Uninstallation complete" + }]`, "", nil) + }, + kubernetesEnvSettings: nil, + namespace: "default", + releaseName: "myrelease", + want: false, + wantErr: false, + }, + { + name: "release exists in pending-install state", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "history") && + strings.Contains(argsStr, "myrelease") && + strings.Contains(argsStr, "--namespace default") && + strings.Contains(argsStr, "--max 1") && + strings.Contains(argsStr, "--output json") + }), + ).Return(`[{ + "revision": 1, + "updated": "2023-01-01T00:00:00Z", + "status": "pending-install", + "chart": "test-chart-1.0.0", + "app_version": "1.0.0", + "description": "Install in progress" + }]`, "", nil) + }, + kubernetesEnvSettings: nil, + namespace: "default", + releaseName: "myrelease", + want: true, + wantErr: false, + }, + { + name: "release not found error in err message", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "history") && + strings.Contains(argsStr, "myrelease") + }), + ).Return("", "", fmt.Errorf("release: not found")) + }, + kubernetesEnvSettings: nil, + namespace: "default", + releaseName: "myrelease", + want: false, + wantErr: false, + }, + { + name: "other command execution error", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "history") && + strings.Contains(argsStr, "myrelease") + }), + ).Return("", "connection refused", fmt.Errorf("exit status 1")) + }, + kubernetesEnvSettings: nil, + namespace: "default", + releaseName: "myrelease", + want: false, + wantErr: true, + }, + { + name: "release exists with kubernetes env settings", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "history") && + strings.Contains(argsStr, "myrelease") && + strings.Contains(argsStr, "--namespace default") && + strings.Contains(argsStr, "--max 1") && + strings.Contains(argsStr, "--output json") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") && + strings.Contains(argsStr, "--kube-context test-context") && + strings.Contains(argsStr, "--kube-token test-token") && + strings.Contains(argsStr, "--kube-as-user test-user") && + strings.Contains(argsStr, "--kube-as-group test-group1") && + strings.Contains(argsStr, "--kube-as-group test-group2") && + strings.Contains(argsStr, "--kube-apiserver https://test-server:6443") && + strings.Contains(argsStr, "--kube-ca-file /tmp/ca.crt") && + strings.Contains(argsStr, "--kube-tls-server-name test-server") && + strings.Contains(argsStr, "--kube-insecure-skip-tls-verify") && + strings.Contains(argsStr, "--burst-limit 100") && + strings.Contains(argsStr, "--qps 50.00") + }), + ).Return(`[{ + "revision": 1, + "updated": "2023-01-01T00:00:00Z", + "status": "deployed", + "chart": "test-chart-1.0.0", + "app_version": "1.0.0", + "description": "Install complete" + }]`, "", nil) + }, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + KubeContext: "test-context", + KubeToken: "test-token", + KubeAsUser: "test-user", + KubeAsGroups: []string{"test-group1", "test-group2"}, + KubeAPIServer: "https://test-server:6443", + KubeCaFile: "/tmp/ca.crt", + KubeTLSServerName: "test-server", + KubeInsecureSkipTLSVerify: true, + BurstLimit: 100, + QPS: 50.0, + }, + namespace: "default", + releaseName: "myrelease", + want: true, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + kubernetesEnvSettings: tt.kubernetesEnvSettings, + } + + exists, err := client.ReleaseExists(t.Context(), tt.namespace, tt.releaseName) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.want, exists) mockExec.AssertExpectations(t) }) } @@ -308,3 +732,782 @@ func Test_cleanUpGenericMap(t *testing.T) { }) } } + +func TestHelmClient_Latest(t *testing.T) { + tests := []struct { + name string + reponame string + chart string + setupMock func(*MockBinaryExecutor) + want string + wantErr bool + }{ + { + name: "valid JSON response", + reponame: "myrepo", + chart: "mychart", + setupMock: func(m *MockBinaryExecutor) { + jsonOutput := `[ + { + "name": "myrepo/mychart", + "version": "1.2.3", + "app_version": "1.2.3", + "description": "A test chart" + } + ]` + m.On("ExecuteCommand", mock.Anything, mock.Anything, mock.Anything, + []string{"search", "repo", "myrepo/mychart", "--version", ">0.0.0", "--versions", "--output", "json"}). + Return(jsonOutput, "", nil) + }, + want: "1.2.3", + wantErr: false, + }, + { + name: "empty results", + reponame: "myrepo", + chart: "nonexistent", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", mock.Anything, mock.Anything, mock.Anything, + []string{"search", "repo", "myrepo/nonexistent", "--version", ">0.0.0", "--versions", "--output", "json"}). + Return("[]", "", nil) + }, + want: "", + wantErr: true, + }, + { + name: "helm command fails", + reponame: "myrepo", + chart: "mychart", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", mock.Anything, mock.Anything, mock.Anything, + []string{"search", "repo", "myrepo/mychart", "--version", ">0.0.0", "--versions", "--output", "json"}). + Return("", "repo not found", assert.AnError) + }, + want: "", + wantErr: true, + }, + { + name: "invalid JSON response", + reponame: "myrepo", + chart: "mychart", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", mock.Anything, mock.Anything, mock.Anything, + []string{"search", "repo", "myrepo/mychart", "--version", ">0.0.0", "--versions", "--output", "json"}). + Return("invalid json", "", nil) + }, + want: "", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + } + + got, err := client.Latest(t.Context(), tt.reponame, tt.chart) + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + assert.Equal(t, tt.want, got) + mockExec.AssertExpectations(t) + }) + } +} + +func TestHelmClient_Upgrade(t *testing.T) { + tests := []struct { + name string + setupMock func(*MockBinaryExecutor) + kubernetesEnvSettings *helmcli.EnvSettings + opts UpgradeOptions + wantErr bool + }{ + { + name: "successful upgrade", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"upgrade", "myrelease", "/path/to/chart", "--namespace", "default", "--wait", "--wait-for-jobs", "--timeout", "5m0s", "--atomic", "--debug"}, + ).Return(`Release "myrelease" has been upgraded.`, "", nil) + }, + kubernetesEnvSettings: nil, // No kubeconfig settings + opts: UpgradeOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + Timeout: 5 * time.Minute, + }, + wantErr: false, + }, + { + name: "upgrade with values", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "upgrade") && + strings.Contains(argsStr, "--values") + }), + ).Return(`Release "myrelease" has been upgraded.`, "", nil) + }, + kubernetesEnvSettings: nil, // No kubeconfig settings + opts: UpgradeOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + Timeout: 5 * time.Minute, + Values: map[string]interface{}{ + "key": "value", + }, + }, + wantErr: false, + }, + { + name: "upgrade with kubernetes env settings", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "upgrade") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") && + strings.Contains(argsStr, "--kube-context test-context") && + strings.Contains(argsStr, "--kube-token test-token") && + strings.Contains(argsStr, "--kube-as-user test-user") && + strings.Contains(argsStr, "--kube-as-group test-group1") && + strings.Contains(argsStr, "--kube-as-group test-group2") && + strings.Contains(argsStr, "--kube-apiserver https://test-server:6443") && + strings.Contains(argsStr, "--kube-ca-file /tmp/ca.crt") && + strings.Contains(argsStr, "--kube-tls-server-name test-server") && + strings.Contains(argsStr, "--kube-insecure-skip-tls-verify") && + strings.Contains(argsStr, "--burst-limit 100") && + strings.Contains(argsStr, "--qps 50.00") + }), + ).Return(`Release "myrelease" has been upgraded.`, "", nil) + }, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + KubeContext: "test-context", + KubeToken: "test-token", + KubeAsUser: "test-user", + KubeAsGroups: []string{"test-group1", "test-group2"}, + KubeAPIServer: "https://test-server:6443", + KubeCaFile: "/tmp/ca.crt", + KubeTLSServerName: "test-server", + KubeInsecureSkipTLSVerify: true, + BurstLimit: 100, + QPS: 50.0, + }, + opts: UpgradeOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + Timeout: 5 * time.Minute, + }, + wantErr: false, + }, + { + name: "upgrade with rollback recovery on another operation in progress", + setupMock: func(m *MockBinaryExecutor) { + // First upgrade attempt fails with "another operation in progress" + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"upgrade", "myrelease", "/path/to/chart", "--namespace", "default", "--wait", "--wait-for-jobs", "--timeout", "3m0s", "--atomic", "--debug"}, + ).Return("", "Error: another operation (install/upgrade/rollback) is in progress", fmt.Errorf("exit status 1")).Once() + + // GetLastRevision call (via ReleaseHistory) + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"history", "myrelease", "--namespace", "default", "--output", "json", "--max", "1"}, + ).Return(`[{"revision": 2, "status": "deployed"}]`, "", nil).Once() + + // Rollback call + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"rollback", "myrelease", "2", "--namespace", "default", "--wait", "--wait-for-jobs", "--timeout", "3m0s", "--debug"}, + ).Return("Rollback was a success! Happy Helming!", "", nil).Once() + + // Second upgrade attempt succeeds after rollback + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"upgrade", "myrelease", "/path/to/chart", "--namespace", "default", "--wait", "--wait-for-jobs", "--timeout", "3m0s", "--atomic", "--debug"}, + ).Return(`Release "myrelease" has been upgraded.`, "", nil).Once() + }, + kubernetesEnvSettings: nil, + opts: UpgradeOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + Timeout: 3 * time.Minute, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + // Create temporary directory for the test + tmpdir, err := os.MkdirTemp("", "helm-test-*") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + tmpdir: tmpdir, + kubernetesEnvSettings: tt.kubernetesEnvSettings, + } + + _, err = client.Upgrade(t.Context(), tt.opts) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + mockExec.AssertExpectations(t) + }) + } +} + +func TestHelmClient_Uninstall(t *testing.T) { + tests := []struct { + name string + setupMock func(*MockBinaryExecutor) + kubernetesEnvSettings *helmcli.EnvSettings + opts UninstallOptions + wantErr bool + }{ + { + name: "successful uninstall", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"uninstall", "myrelease", "--namespace", "default", "--debug"}, + ).Return(`release "myrelease" uninstalled`, "", nil) + }, + kubernetesEnvSettings: nil, // No kubeconfig settings + opts: UninstallOptions{ + ReleaseName: "myrelease", + Namespace: "default", + }, + wantErr: false, + }, + { + name: "uninstall with kubernetes env settings", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "uninstall") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") && + strings.Contains(argsStr, "--kube-context test-context") && + strings.Contains(argsStr, "--kube-token test-token") && + strings.Contains(argsStr, "--kube-as-user test-user") && + strings.Contains(argsStr, "--kube-as-group test-group1") && + strings.Contains(argsStr, "--kube-as-group test-group2") && + strings.Contains(argsStr, "--kube-apiserver https://test-server:6443") && + strings.Contains(argsStr, "--kube-ca-file /tmp/ca.crt") && + strings.Contains(argsStr, "--kube-tls-server-name test-server") && + strings.Contains(argsStr, "--kube-insecure-skip-tls-verify") && + strings.Contains(argsStr, "--burst-limit 100") && + strings.Contains(argsStr, "--qps 50.00") + }), + ).Return(`release "myrelease" uninstalled`, "", nil) + }, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + KubeContext: "test-context", + KubeToken: "test-token", + KubeAsUser: "test-user", + KubeAsGroups: []string{"test-group1", "test-group2"}, + KubeAPIServer: "https://test-server:6443", + KubeCaFile: "/tmp/ca.crt", + KubeTLSServerName: "test-server", + KubeInsecureSkipTLSVerify: true, + BurstLimit: 100, + QPS: 50.0, + }, + opts: UninstallOptions{ + ReleaseName: "myrelease", + Namespace: "default", + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + kubernetesEnvSettings: tt.kubernetesEnvSettings, + } + + err := client.Uninstall(t.Context(), tt.opts) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + mockExec.AssertExpectations(t) + }) + } +} + +func TestHelmClient_Render(t *testing.T) { + tests := []struct { + name string + setupMock func(*MockBinaryExecutor) + kubernetesEnvSettings *helmcli.EnvSettings + opts InstallOptions + wantErr bool + }{ + { + name: "successful render", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"template", "myrelease", "/path/to/chart", "--namespace", "default", "--create-namespace", "--include-crds", "--debug"}, + ).Return(`--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-config`, "", nil) + }, + kubernetesEnvSettings: nil, // No kubeconfig settings + opts: InstallOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + }, + wantErr: false, + }, + { + name: "render with values", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "template") && + strings.Contains(argsStr, "--values") + }), + ).Return(`--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-config`, "", nil) + }, + kubernetesEnvSettings: nil, // No kubeconfig settings + opts: InstallOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + Values: map[string]interface{}{ + "key": "value", + }, + }, + wantErr: false, + }, + { + name: "render with kubernetes env settings", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "template") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") && + strings.Contains(argsStr, "--kube-context test-context") && + strings.Contains(argsStr, "--kube-token test-token") && + strings.Contains(argsStr, "--kube-as-user test-user") && + strings.Contains(argsStr, "--kube-as-group test-group1") && + strings.Contains(argsStr, "--kube-as-group test-group2") && + strings.Contains(argsStr, "--kube-apiserver https://test-server:6443") && + strings.Contains(argsStr, "--kube-ca-file /tmp/ca.crt") && + strings.Contains(argsStr, "--kube-tls-server-name test-server") && + strings.Contains(argsStr, "--kube-insecure-skip-tls-verify") && + strings.Contains(argsStr, "--burst-limit 100") && + strings.Contains(argsStr, "--qps 50.00") + }), + ).Return(`--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-config`, "", nil) + }, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + KubeContext: "test-context", + KubeToken: "test-token", + KubeAsUser: "test-user", + KubeAsGroups: []string{"test-group1", "test-group2"}, + KubeAPIServer: "https://test-server:6443", + KubeCaFile: "/tmp/ca.crt", + KubeTLSServerName: "test-server", + KubeInsecureSkipTLSVerify: true, + BurstLimit: 100, + QPS: 50.0, + }, + opts: InstallOptions{ + ReleaseName: "myrelease", + ChartPath: "/path/to/chart", + Namespace: "default", + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + // Create temporary directory for the test + tmpdir, err := os.MkdirTemp("", "helm-test-*") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + tmpdir: tmpdir, + kubernetesEnvSettings: tt.kubernetesEnvSettings, + } + + _, err = client.Render(t.Context(), tt.opts) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + mockExec.AssertExpectations(t) + }) + } +} +func TestHelmClient_ReleaseHistory(t *testing.T) { + tests := []struct { + name string + setupMock func(*MockBinaryExecutor) + kubernetesEnvSettings *helmcli.EnvSettings + namespace string + releaseName string + maxRevisions int + wantErr bool + }{ + { + name: "successful history retrieval", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"history", "myrelease", "--namespace", "default", "--output", "json", "--max", "5"}, + ).Return(`[{"revision": 1, "status": "superseded"}, {"revision": 2, "status": "superseded"}, {"revision": 3, "status": "deployed"}]`, "", nil) + }, + kubernetesEnvSettings: nil, + namespace: "default", + releaseName: "myrelease", + maxRevisions: 5, + wantErr: false, + }, + { + name: "history with kubernetes env settings", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "history") && + strings.Contains(argsStr, "myrelease") && + strings.Contains(argsStr, "--namespace default") && + strings.Contains(argsStr, "--output json") && + strings.Contains(argsStr, "--max 3") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") && + strings.Contains(argsStr, "--kube-context test-context") && + strings.Contains(argsStr, "--kube-token test-token") && + strings.Contains(argsStr, "--kube-as-user test-user") && + strings.Contains(argsStr, "--kube-as-group test-group1") && + strings.Contains(argsStr, "--kube-as-group test-group2") && + strings.Contains(argsStr, "--kube-apiserver https://test-server:6443") && + strings.Contains(argsStr, "--kube-ca-file /tmp/ca.crt") && + strings.Contains(argsStr, "--kube-tls-server-name test-server") && + strings.Contains(argsStr, "--kube-insecure-skip-tls-verify") && + strings.Contains(argsStr, "--burst-limit 100") && + strings.Contains(argsStr, "--qps 50.00") + }), + ).Return(`[{"revision": 1, "status": "deployed"}]`, "", nil) + }, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + KubeContext: "test-context", + KubeToken: "test-token", + KubeAsUser: "test-user", + KubeAsGroups: []string{"test-group1", "test-group2"}, + KubeAPIServer: "https://test-server:6443", + KubeCaFile: "/tmp/ca.crt", + KubeTLSServerName: "test-server", + KubeInsecureSkipTLSVerify: true, + BurstLimit: 100, + QPS: 50.0, + }, + namespace: "default", + releaseName: "myrelease", + maxRevisions: 3, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + kubernetesEnvSettings: tt.kubernetesEnvSettings, + } + + _, err := client.ReleaseHistory(t.Context(), tt.namespace, tt.releaseName, tt.maxRevisions) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + mockExec.AssertExpectations(t) + }) + } +} + +func TestHelmClient_GetLastRevision(t *testing.T) { + tests := []struct { + name string + setupMock func(*MockBinaryExecutor) + kubernetesEnvSettings *helmcli.EnvSettings + namespace string + releaseName string + wantErr bool + }{ + { + name: "successful get last revision", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"history", "myrelease", "--namespace", "default", "--output", "json", "--max", "1"}, + ).Return(`[{"revision": 3, "status": "deployed"}]`, "", nil) + }, + kubernetesEnvSettings: nil, + namespace: "default", + releaseName: "myrelease", + wantErr: false, + }, + { + name: "get last revision with kubeconfig", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "history") && + strings.Contains(argsStr, "myrelease") && + strings.Contains(argsStr, "--namespace default") && + strings.Contains(argsStr, "--output json") && + strings.Contains(argsStr, "--max 1") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") && + strings.Contains(argsStr, "--kube-context test-context") && + strings.Contains(argsStr, "--kube-token test-token") && + strings.Contains(argsStr, "--kube-as-user test-user") && + strings.Contains(argsStr, "--kube-as-group test-group1") && + strings.Contains(argsStr, "--kube-as-group test-group2") && + strings.Contains(argsStr, "--kube-apiserver https://test-server:6443") && + strings.Contains(argsStr, "--kube-ca-file /tmp/ca.crt") && + strings.Contains(argsStr, "--kube-tls-server-name test-server") && + strings.Contains(argsStr, "--kube-insecure-skip-tls-verify") && + strings.Contains(argsStr, "--burst-limit 100") && + strings.Contains(argsStr, "--qps 50.00") + }), + ).Return(`[{"revision": 5, "status": "deployed"}]`, "", nil) + }, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + KubeContext: "test-context", + KubeToken: "test-token", + KubeAsUser: "test-user", + KubeAsGroups: []string{"test-group1", "test-group2"}, + KubeAPIServer: "https://test-server:6443", + KubeCaFile: "/tmp/ca.crt", + KubeTLSServerName: "test-server", + KubeInsecureSkipTLSVerify: true, + BurstLimit: 100, + QPS: 50.0, + }, + namespace: "default", + releaseName: "myrelease", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + kubernetesEnvSettings: tt.kubernetesEnvSettings, + } + + _, err := client.GetLastRevision(t.Context(), tt.namespace, tt.releaseName) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + mockExec.AssertExpectations(t) + }) + } +} + +func TestHelmClient_Rollback(t *testing.T) { + tests := []struct { + name string + setupMock func(*MockBinaryExecutor) + kubernetesEnvSettings *helmcli.EnvSettings + opts RollbackOptions + wantErr bool + }{ + { + name: "successful rollback", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + []string{"rollback", "myrelease", "2", "--namespace", "default", "--wait", "--wait-for-jobs", "--timeout", "5m0s", "--debug"}, + ).Return("Rollback was a success! Happy Helming!", "", nil) + }, + kubernetesEnvSettings: nil, + opts: RollbackOptions{ + ReleaseName: "myrelease", + Namespace: "default", + Revision: 2, + Timeout: 5 * time.Minute, + }, + wantErr: false, + }, + { + name: "rollback with kubeconfig", + setupMock: func(m *MockBinaryExecutor) { + m.On("ExecuteCommand", + mock.Anything, // context + mock.Anything, // env + mock.Anything, // LogFn + mock.MatchedBy(func(args []string) bool { + argsStr := strings.Join(args, " ") + return strings.HasPrefix(argsStr, "rollback") && + strings.Contains(argsStr, "myrelease") && + strings.Contains(argsStr, "3") && + strings.Contains(argsStr, "--namespace default") && + strings.Contains(argsStr, "--wait") && + strings.Contains(argsStr, "--wait-for-jobs") && + strings.Contains(argsStr, "--timeout 5m0s") && + strings.Contains(argsStr, "--debug") && + strings.Contains(argsStr, "--kubeconfig /tmp/test-kubeconfig") + }), + ).Return("Rollback was a success! Happy Helming!", "", nil) + }, + kubernetesEnvSettings: &helmcli.EnvSettings{ + KubeConfig: "/tmp/test-kubeconfig", + }, + opts: RollbackOptions{ + ReleaseName: "myrelease", + Namespace: "default", + Revision: 3, + Timeout: 5 * time.Minute, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockExec := &MockBinaryExecutor{} + tt.setupMock(mockExec) + + client := &HelmClient{ + helmPath: "/usr/local/bin/helm", + executor: mockExec, + kubernetesEnvSettings: tt.kubernetesEnvSettings, + } + + _, err := client.Rollback(t.Context(), tt.opts) + + if tt.wantErr { + assert.Error(t, err) + return + } + + require.NoError(t, err) + mockExec.AssertExpectations(t) + }) + } +} diff --git a/pkg/helm/interface.go b/pkg/helm/interface.go index 5a45d10314..e1ce8e85d8 100644 --- a/pkg/helm/interface.go +++ b/pkg/helm/interface.go @@ -4,7 +4,6 @@ import ( "context" "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/repo" ) @@ -15,16 +14,15 @@ var ( type Client interface { Close() error AddRepo(ctx context.Context, repo *repo.Entry) error - AddRepoBin(ctx context.Context, repo *repo.Entry) error Latest(ctx context.Context, reponame, chart string) (string, error) Pull(ctx context.Context, reponame, chart string, version string) (string, error) PullByRef(ctx context.Context, ref string, version string) (string, error) RegistryAuth(ctx context.Context, server, user, pass string) error Push(ctx context.Context, path, dst string) error - GetChartMetadata(ctx context.Context, ref string, version string) (*chart.Metadata, error) + GetChartMetadata(ctx context.Context, chartPath string, version string) (*chart.Metadata, error) ReleaseExists(ctx context.Context, namespace string, releaseName string) (bool, error) - Install(ctx context.Context, opts InstallOptions) (*release.Release, error) - Upgrade(ctx context.Context, opts UpgradeOptions) (*release.Release, error) + Install(ctx context.Context, opts InstallOptions) (string, error) + Upgrade(ctx context.Context, opts UpgradeOptions) (string, error) Uninstall(ctx context.Context, opts UninstallOptions) error Render(ctx context.Context, opts InstallOptions) ([][]byte, error) } diff --git a/pkg/helm/mock_client.go b/pkg/helm/mock_client.go index f395b02a99..c9d907f705 100644 --- a/pkg/helm/mock_client.go +++ b/pkg/helm/mock_client.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/mock" "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/repo" ) @@ -25,11 +24,6 @@ func (m *MockClient) AddRepo(ctx context.Context, repo *repo.Entry) error { return args.Error(0) } -func (m *MockClient) AddRepoBin(ctx context.Context, repo *repo.Entry) error { - args := m.Called(ctx, repo) - return args.Error(0) -} - func (m *MockClient) Latest(ctx context.Context, reponame, chart string) (string, error) { args := m.Called(ctx, reponame, chart) return args.String(0), args.Error(1) @@ -55,8 +49,8 @@ func (m *MockClient) Push(ctx context.Context, path, dst string) error { return args.Error(0) } -func (m *MockClient) GetChartMetadata(ctx context.Context, ref string, version string) (*chart.Metadata, error) { - args := m.Called(ctx, ref, version) +func (m *MockClient) GetChartMetadata(ctx context.Context, chartPath string, version string) (*chart.Metadata, error) { + args := m.Called(ctx, chartPath, version) if args.Get(0) == nil { return nil, args.Error(1) } @@ -68,20 +62,20 @@ func (m *MockClient) ReleaseExists(ctx context.Context, namespace string, releas return args.Bool(0), args.Error(1) } -func (m *MockClient) Install(ctx context.Context, opts InstallOptions) (*release.Release, error) { +func (m *MockClient) Install(ctx context.Context, opts InstallOptions) (string, error) { args := m.Called(ctx, opts) if args.Get(0) == nil { - return nil, args.Error(1) + return "", args.Error(1) } - return args.Get(0).(*release.Release), args.Error(1) + return args.Get(0).(string), args.Error(1) } -func (m *MockClient) Upgrade(ctx context.Context, opts UpgradeOptions) (*release.Release, error) { +func (m *MockClient) Upgrade(ctx context.Context, opts UpgradeOptions) (string, error) { args := m.Called(ctx, opts) if args.Get(0) == nil { - return nil, args.Error(1) + return "", args.Error(1) } - return args.Get(0).(*release.Release), args.Error(1) + return args.Get(0).(string), args.Error(1) } func (m *MockClient) Uninstall(ctx context.Context, opts UninstallOptions) error { diff --git a/tests/dryrun/Dockerfile b/tests/dryrun/Dockerfile index 50a3e3c978..49777d431d 100644 --- a/tests/dryrun/Dockerfile +++ b/tests/dryrun/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.25.4-alpine AS build +FROM golang:1.25.3-alpine AS build RUN apk add --no-cache ca-certificates curl git make bash diff --git a/tests/dryrun/v3_install_test.go b/tests/dryrun/v3_install_test.go index 9f8370c8e0..5ac13dda02 100644 --- a/tests/dryrun/v3_install_test.go +++ b/tests/dryrun/v3_install_test.go @@ -179,12 +179,11 @@ func validateHappyPathOnline(t *testing.T, hcli *helm.MockClient) { adminConsoleOpts, found := isHelmReleaseInstalled(hcli, "admin-console") require.True(t, found, "admin-console helm release should be installed") assertHelmValues(t, adminConsoleOpts.Values, map[string]any{ - "isAirgap": false, - "isMultiNodeEnabled": true, - "embeddedClusterID": in.Spec.ClusterID, - // TODO: enable this once we stop relying on KOTS to deploy the app - // "isEmbeddedClusterV3": true, - "kurlProxy.enabled": false, + "isAirgap": false, + "isMultiNodeEnabled": true, + "embeddedClusterID": in.Spec.ClusterID, + "isEmbeddedClusterV3": true, + "kurlProxy.enabled": false, }) // Validate that registry addon is NOT installed for online installations @@ -194,6 +193,9 @@ func validateHappyPathOnline(t *testing.T, hcli *helm.MockClient) { // Validate that registry-creds secret is NOT created for online installations assertSecretNotExists(t, kcli, "registry-creds", adminConsoleNamespace) + // Validate that image pull secret IS created for online installations + assertSecretExists(t, kcli, "fake-app-slug-registry", adminConsoleNamespace) + // Validate OS environment variables use default data directory assertEnv(t, dr.OSEnv, map[string]string{ "TMPDIR": "/var/lib/fake-app-slug/tmp", @@ -223,13 +225,11 @@ func validateHappyPathOnline(t *testing.T, hcli *helm.MockClient) { }, }) - // Validate that KOTS CLI install command is present - assertCommands(t, dr.Commands, - []any{ - regexp.MustCompile(`kubectl-kots.* install fake-app-slug/fake-channel-slug .*`), - }, - false, - ) + // Validate that app charts are installed via Helm + _, found = isHelmReleaseInstalled(hcli, "nginx-app") + require.True(t, found, "nginx-app helm release should be installed") + _, found = isHelmReleaseInstalled(hcli, "redis-app") + require.True(t, found, "redis-app helm release should be installed") } func TestV3InstallHeadless_HappyPathAirgap(t *testing.T) { @@ -254,7 +254,7 @@ func TestV3InstallHeadless_HappyPathAirgap(t *testing.T) { require.NoError(t, err, "headless installation should succeed") - validateHappyPathAirgap(t, hcli, airgapBundleFile) + validateHappyPathAirgap(t, hcli) if !t.Failed() { t.Logf("V3 headless airgap installation test passed") @@ -294,14 +294,14 @@ func TestV3Install_HappyPathAirgap(t *testing.T) { ignoreAppPreflights: false, }) - validateHappyPathAirgap(t, hcli, airgapBundleFile) + validateHappyPathAirgap(t, hcli) if !t.Failed() { t.Logf("V3 airgap installation test passed") } } -func validateHappyPathAirgap(t *testing.T, hcli *helm.MockClient, airgapBundleFile string) { +func validateHappyPathAirgap(t *testing.T, hcli *helm.MockClient) { t.Helper() adminConsoleNamespace := "fake-app-slug" @@ -383,17 +383,14 @@ func validateHappyPathAirgap(t *testing.T, hcli *helm.MockClient, airgapBundleFi // Validate that registry-creds secret IS created for airgap installations assertSecretExists(t, kcli, "registry-creds", adminConsoleNamespace) - // Validate that KOTS CLI install command includes --airgap-bundle flag for airgap installations - // The --airgap-bundle flag flows through: Installer → Install Controller → App Install Manager - // The App Install Manager uses it to set kotscli.InstallOptions.AirgapBundle (install.go:68) - // This ensures the KOTS installer receives the airgap bundle path - assertCommands(t, dr.Commands, - []any{ - // KOTS install command should contain --airgap-bundle with the correct path - regexp.MustCompile(fmt.Sprintf(`kubectl-kots.* install fake-app-slug/fake-channel-slug .* --airgap-bundle %s`, regexp.QuoteMeta(airgapBundleFile))), - }, - false, - ) + // Validate that image pull secret IS created for airgap installations + assertSecretExists(t, kcli, "fake-app-slug-registry", adminConsoleNamespace) + + // Validate that app charts are installed via Helm for airgap installations + _, found = isHelmReleaseInstalled(hcli, "nginx-app") + require.True(t, found, "nginx-app helm release should be installed") + _, found = isHelmReleaseInstalled(hcli, "redis-app") + require.True(t, found, "redis-app helm release should be installed") } func TestV3InstallHeadless_Metrics(t *testing.T) { diff --git a/utils/go.mod b/utils/go.mod index ef8096047d..2e989fa876 100644 --- a/utils/go.mod +++ b/utils/go.mod @@ -1,6 +1,6 @@ module github.com/replicatedhq/embedded-cluster/utils -go 1.25.4 +go 1.25.3 require github.com/stretchr/testify v1.11.1 diff --git a/versions.mk b/versions.mk index c8257bba6c..a08a548706 100644 --- a/versions.mk +++ b/versions.mk @@ -20,14 +20,14 @@ K0S_GO_VERSION = $(K0S_VERSION_1_$(K0S_MINOR_VERSION)) TROUBLESHOOT_VERSION = v0.122.0 # Helm Version -HELM_VERSION = v3.19.2 +HELM_VERSION = v3.19.0 # FIO Version (for performance testing) FIO_VERSION = 3.41 # Kubernetes Development Tool Versions CONTROLLER_TOOLS_VERSION = v0.19.0 -KUSTOMIZE_VERSION = v5.8.0 +KUSTOMIZE_VERSION = v5.7.1 ### Overrides ### diff --git a/web/package-lock.json b/web/package-lock.json index eddee5b26b..8d66938a39 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,20 +11,20 @@ "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.17", "@tailwindcss/typography": "^0.5.19", - "@tanstack/react-query": "^5.90.12", - "lucide-react": "^0.556.0", + "@tanstack/react-query": "^5.90.11", + "lucide-react": "^0.555.0", "openapi-fetch": "^0.15.0", "react": "^19.1.2", - "react-dom": "^19.2.1", + "react-dom": "^19.1.2", "react-markdown": "^10.1.0", - "react-router-dom": "^7.10.1", + "react-router-dom": "^7.9.6", "remark-gfm": "^4.0.1" }, "devDependencies": { "@eslint/js": "^9.39.1", "@faker-js/faker": "^10.1.0", "@netlify/functions": "^5.0.1", - "@netlify/vite-plugin": "^2.7.15", + "@netlify/vite-plugin": "^2.7.14", "@tailwindcss/vite": "^4.1.17", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", @@ -41,14 +41,14 @@ "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", "jsdom": "^27.1.0", - "msw": "2.12.4", + "msw": "2.12.3", "openapi-backend": "^5.15.0", "openapi-typescript": "^7.10.1", "tailwindcss": "^4.1.13", "typescript": "^5.9.3", - "typescript-eslint": "^8.48.1", + "typescript-eslint": "^8.48.0", "unified": "^11.0.5", - "vite": "^7.2.6", + "vite": "^7.2.4", "vite-plugin-static-copy": "^3.1.4", "vitest": "^3.2.4" } @@ -183,7 +183,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -543,7 +542,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -587,7 +585,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2029,6 +2026,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": "20 || >=22" } @@ -2040,6 +2038,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@isaacs/balanced-match": "^4.0.1" }, @@ -2130,9 +2129,9 @@ "license": "MIT" }, "node_modules/@mapbox/node-pre-gyp": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", - "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz", + "integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2242,28 +2241,28 @@ } }, "node_modules/@netlify/ai": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@netlify/ai/-/ai-0.3.4.tgz", - "integrity": "sha512-mV0RtkO5dOwbuqRn/Sn0aHIV4j6sw8B4F16WCx0GYBRcJ9IbBkzvuEzW0IDUbNE6hxu9FFs5WRDASDJpgDY1ZQ==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@netlify/ai/-/ai-0.3.3.tgz", + "integrity": "sha512-P2EWy+fmybtJ4xptqR1qq+fL0JK4kj7MxqziSIB9BfqG7kIGDPTHHgbrzpEbgNvUpPt/l74mF5rQ5UW26VRp3A==", "dev": true, "dependencies": { - "@netlify/api": "^14.0.11" + "@netlify/api": "^14.0.10" }, "engines": { "node": ">=20.6.1" }, "peerDependencies": { - "@netlify/api": ">=14.0.11" + "@netlify/api": ">=14.0.10" } }, "node_modules/@netlify/api": { - "version": "14.0.12", - "resolved": "https://registry.npmjs.org/@netlify/api/-/api-14.0.12.tgz", - "integrity": "sha512-4xSfHAj9PIZZ78YOPby6TBHxYnf6sOE1/jpkHSDyt2oRxF94qJ0fhp96Fo2kq/rIhvgTlU5Ce3HARi8BDY4mLw==", + "version": "14.0.10", + "resolved": "https://registry.npmjs.org/@netlify/api/-/api-14.0.10.tgz", + "integrity": "sha512-2zPdZzayOGMff/atHyNE8y060R/PuWfvsFzywe9cW2hLzfJtYy1+hFFrfZS6a1KL5RkqsAFd8OBEO0s+ospDWA==", "dev": true, "license": "MIT", "dependencies": { - "@netlify/open-api": "^2.45.0", + "@netlify/open-api": "^2.43.1", "node-fetch": "^3.0.0", "p-wait-for": "^5.0.0", "picoquery": "^2.5.0" @@ -2280,14 +2279,14 @@ "license": "Apache 2" }, "node_modules/@netlify/blobs": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-10.4.2.tgz", - "integrity": "sha512-IySDzwNTtI7jehxkbc1bnODiIcmIaamG8YBf1YtXmxsBlQGcAKdkN6QSulHYQ+quSEK+W5qDecwV6BjUs5094A==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-10.4.1.tgz", + "integrity": "sha512-43wntITwgocQxJThPxtgZ9XCPk2wAXrqD61uqgcIG2EhT/cXpvfEGEo6nLXXtb9irW6nQVXusJeLHEhKJFC4Vg==", "dev": true, "license": "MIT", "dependencies": { "@netlify/dev-utils": "4.3.2", - "@netlify/otel": "^5.0.1", + "@netlify/otel": "^5.0.0", "@netlify/runtime-utils": "2.2.1" }, "engines": { @@ -2308,16 +2307,16 @@ } }, "node_modules/@netlify/config": { - "version": "24.1.2", - "resolved": "https://registry.npmjs.org/@netlify/config/-/config-24.1.2.tgz", - "integrity": "sha512-ZnJTi/BZRONWQ9JcDIv2RYUIlEoxHAvLodtp9DzI3EwWoalh/3XJlA7kDuC/JsKqlZWfm2xddAl7W8ZKus7gpQ==", + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/@netlify/config/-/config-23.2.0.tgz", + "integrity": "sha512-zlI792/efPUY1XKtBML2OJBgKMyfNQIeGEYibH8SqeDxPjNuCy0qELE0U9Sc6+Ss34XryPBUPdV60tYhSoe6lw==", "dev": true, "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", - "@netlify/api": "^14.0.12", - "@netlify/headers-parser": "^9.0.2", - "@netlify/redirect-parser": "^15.0.3", + "@netlify/api": "^14.0.3", + "@netlify/headers-parser": "^9.0.1", + "@netlify/redirect-parser": "^15.0.2", "chalk": "^5.0.0", "cron-parser": "^4.1.0", "deepmerge": "^4.2.2", @@ -2337,8 +2336,7 @@ "tomlify-j0.4": "^3.0.0", "validate-npm-package-name": "^5.0.0", "yaml": "^2.8.0", - "yargs": "^17.6.0", - "zod": "^4.0.5" + "yargs": "^17.6.0" }, "bin": { "netlify-config": "bin.js" @@ -2463,22 +2461,22 @@ } }, "node_modules/@netlify/dev": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/@netlify/dev/-/dev-4.8.3.tgz", - "integrity": "sha512-x3N6tflo9daQH6gxZrvUwfxe1/Z7cdmPQuyOmw8NHKPDu4+JQHK41BcJdIUDBzeKxKjfKyhGautYjXVML3//wg==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/@netlify/dev/-/dev-4.8.2.tgz", + "integrity": "sha512-XY+TylQZwqm0mDHz4N6ovSAWE0T/dkjMu7y/yr7idG12hMscfr4dOvkLEn5LiueMrbneAwpte1kkNcygRa0dUw==", "dev": true, "license": "MIT", "dependencies": { - "@netlify/ai": "^0.3.4", - "@netlify/blobs": "10.4.2", - "@netlify/config": "^24.1.1", + "@netlify/ai": "^0.3.3", + "@netlify/blobs": "10.4.1", + "@netlify/config": "^23.2.0", "@netlify/dev-utils": "4.3.2", - "@netlify/edge-functions-dev": "1.0.6", - "@netlify/functions-dev": "1.1.3", + "@netlify/edge-functions-dev": "1.0.5", + "@netlify/functions-dev": "1.1.2", "@netlify/headers": "2.1.2", "@netlify/images": "1.3.2", "@netlify/redirects": "3.1.3", - "@netlify/runtime": "4.1.10", + "@netlify/runtime": "4.1.9", "@netlify/static": "3.1.2", "ulid": "^3.0.0" }, @@ -2557,9 +2555,9 @@ } }, "node_modules/@netlify/edge-bundler": { - "version": "14.9.1", - "resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-14.9.1.tgz", - "integrity": "sha512-EFkoPlCqBnoFKo8XyTw9nI91DBQAhms398tXKThecAXI/smrHiv4/sGYXj2ISHR022Aoioll8K9U6aLvmQgDAg==", + "version": "14.9.0", + "resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-14.9.0.tgz", + "integrity": "sha512-EyEzPiVH8xubRQrxyp9j9aZLOkg3WsfMabgQlXMhPnh4I78ZykjVHiERFr71bSvWQH1GE5sBLoATSgmjLJv5yw==", "dev": true, "license": "MIT", "dependencies": { @@ -2569,7 +2567,7 @@ "better-ajv-errors": "^1.2.0", "common-path-prefix": "^3.0.0", "env-paths": "^3.0.0", - "esbuild": "0.27.1", + "esbuild": "0.25.11", "execa": "^8.0.0", "find-up": "^7.0.0", "get-port": "^7.0.0", @@ -2588,1431 +2586,504 @@ "node": ">=18.14.0" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", - "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", - "cpu": [ - "ppc64" - ], + "node_modules/@netlify/edge-bundler/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/android-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", - "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", - "cpu": [ - "arm" - ], + "node_modules/@netlify/edge-bundler/node_modules/ajv-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "peerDependencies": { + "ajv": "^8.0.1" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/android-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", - "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/edge-bundler/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, "engines": { "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/android-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", - "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/edge-bundler/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@netlify/edge-bundler/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "p-locate": "^6.0.0" + }, "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", - "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/edge-bundler/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "yocto-queue": "^1.0.0" + }, "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/darwin-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", - "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/edge-bundler/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "p-limit": "^4.0.0" + }, "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", - "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/edge-bundler/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", - "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/edge-bundler/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", - "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", - "cpu": [ - "arm" - ], + "node_modules/@netlify/edge-bundler/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=18" + "node": ">=10" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", - "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/edge-bundler/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], - "engines": { - "node": ">=18" + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", - "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", - "cpu": [ - "ia32" - ], + "node_modules/@netlify/edge-bundler/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=18" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-loong64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", - "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", - "cpu": [ - "loong64" - ], + "node_modules/@netlify/edge-functions": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@netlify/edge-functions/-/edge-functions-3.0.2.tgz", + "integrity": "sha512-1vW3R+Rc2JxL6qITndlT87N94GPjJ6gH2ntXW3IDdLzSABoU9XCHw4lRzDw+bhgSLTm0oyOwQA2+hhFvstznNQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@netlify/types": "2.2.0" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", - "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", - "cpu": [ - "mips64el" - ], + "node_modules/@netlify/edge-functions-bootstrap": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@netlify/edge-functions-bootstrap/-/edge-functions-bootstrap-2.16.0.tgz", + "integrity": "sha512-v8QQihSbBHj3JxtJsHoepXALpNumD9M7egHoc8z62FYl5it34dWczkaJoFFopEyhiBVKi4K/n0ZYpdzwfujd6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@netlify/edge-functions-dev": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@netlify/edge-functions-dev/-/edge-functions-dev-1.0.5.tgz", + "integrity": "sha512-rgpNmMq4oGGvVs4Rkqtwl1NJE7dV0ki+EXz98ReVo20snCam60iXkGKLoLtKtQzFg1e0pkz+UNq5CafC4zFTNw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "@netlify/dev-utils": "4.3.2", + "@netlify/edge-bundler": "^14.8.7", + "@netlify/edge-functions": "3.0.2", + "@netlify/edge-functions-bootstrap": "2.16.0", + "@netlify/runtime-utils": "2.2.1", + "get-port": "^7.1.0" + }, "engines": { - "node": ">=18" + "node": ">=20.6.1" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", - "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", - "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-s390x": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", - "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", - "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", - "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", - "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-5.1.0.tgz", + "integrity": "sha512-LZtiQtf/QzPHIeNDZuIBxx04kmU7lCipWqZ26ejX7mYSB3yj2wvpZfF49kD8B8FoKTydSvgFmBpIcCO5FvpEXA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "@netlify/types": "2.2.0" + }, "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", - "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/functions-dev": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@netlify/functions-dev/-/functions-dev-1.1.2.tgz", + "integrity": "sha512-O3Exq9yomIBsar7Yv7MkJ3qHQNLy3gAumuqzQ5DVyf7pMfNpyPmZCPF+bdVQdIGt5qrhNtUZYL2VIZgf+YOVgg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "@netlify/blobs": "10.4.1", + "@netlify/dev-utils": "4.3.2", + "@netlify/functions": "5.1.0", + "@netlify/zip-it-and-ship-it": "^14.1.13", + "cron-parser": "^4.9.0", + "decache": "^4.6.2", + "extract-zip": "^2.0.1", + "is-stream": "^4.0.1", + "jwt-decode": "^4.0.0", + "lambda-local": "^2.2.0", + "read-package-up": "^11.0.0", + "semver": "^7.6.3", + "source-map-support": "^0.5.21" + }, "engines": { - "node": ">=18" + "node": ">=20.6.1" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", - "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/functions-dev/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=18" + "node": ">=10" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", - "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/headers": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@netlify/headers/-/headers-2.1.2.tgz", + "integrity": "sha512-3dkP1LU9U3ynKHuLUP0HCzJhf0bLs/ESuz1QjnUTOwx6oxc3hkIFa8B8wAa00uOrx3lVgcN76Zuydvkjulk7wA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "dependencies": { + "@netlify/headers-parser": "^9.0.2" + }, "engines": { - "node": ">=18" + "node": ">=20.6.1" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/sunos-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", - "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/headers-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@netlify/headers-parser/-/headers-parser-9.0.2.tgz", + "integrity": "sha512-86YEGPxVemhksY1LeSr8NSOyH11RHvYHq+FuBJnTlPZoRDX+TD+0TAxF6lwzAgVTd1VPkyFEHlNgUGqw7aNzRQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "@iarna/toml": "^2.2.5", + "escape-string-regexp": "^5.0.0", + "fast-safe-stringify": "^2.0.7", + "is-plain-obj": "^4.0.0", + "map-obj": "^5.0.0", + "path-exists": "^5.0.0" + }, "engines": { - "node": ">=18" + "node": ">=18.14.0" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/win32-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", - "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/headers-parser/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/win32-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", - "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", - "cpu": [ - "ia32" - ], + "node_modules/@netlify/images": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@netlify/images/-/images-1.3.2.tgz", + "integrity": "sha512-/YEhpm0KbiDtbYZbfOv6tbJyk+/j10tndeJAvUc94mSyv3EB7X4DREvhftZcxLuE6A2IceaKIbh5DpUNgJHjxA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "ipx": "^3.1.1" + }, "engines": { - "node": ">=18" + "node": ">=20.6.1" } }, - "node_modules/@netlify/edge-bundler/node_modules/@esbuild/win32-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", - "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/open-api": { + "version": "2.43.1", + "resolved": "https://registry.npmjs.org/@netlify/open-api/-/open-api-2.43.1.tgz", + "integrity": "sha512-MPhzLfVVTzQCs9iIjxxgIoXv6/tE2FVHTFT2gi4leChj5o4DQ9an/6gc1q7QPRXeIuPIb+P6AaYi3TGHf8vsBA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": ">=14.8.0" } }, - "node_modules/@netlify/edge-bundler/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/@netlify/otel": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-5.0.0.tgz", + "integrity": "sha512-7EWbS+puDub800IQ9MUVcLrWwCNPyK/u1Rs08f0Y+O4dBGVkuTm/RyaLoU58PPLuNCfPHXebfIYFZxN+/CtZeA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/ajv-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", - "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.1" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/esbuild": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", - "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "@opentelemetry/api": "1.9.0", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-node": "1.30.1" }, "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.1", - "@esbuild/android-arm": "0.27.1", - "@esbuild/android-arm64": "0.27.1", - "@esbuild/android-x64": "0.27.1", - "@esbuild/darwin-arm64": "0.27.1", - "@esbuild/darwin-x64": "0.27.1", - "@esbuild/freebsd-arm64": "0.27.1", - "@esbuild/freebsd-x64": "0.27.1", - "@esbuild/linux-arm": "0.27.1", - "@esbuild/linux-arm64": "0.27.1", - "@esbuild/linux-ia32": "0.27.1", - "@esbuild/linux-loong64": "0.27.1", - "@esbuild/linux-mips64el": "0.27.1", - "@esbuild/linux-ppc64": "0.27.1", - "@esbuild/linux-riscv64": "0.27.1", - "@esbuild/linux-s390x": "0.27.1", - "@esbuild/linux-x64": "0.27.1", - "@esbuild/netbsd-arm64": "0.27.1", - "@esbuild/netbsd-x64": "0.27.1", - "@esbuild/openbsd-arm64": "0.27.1", - "@esbuild/openbsd-x64": "0.27.1", - "@esbuild/openharmony-arm64": "0.27.1", - "@esbuild/sunos-x64": "0.27.1", - "@esbuild/win32-arm64": "0.27.1", - "@esbuild/win32-ia32": "0.27.1", - "@esbuild/win32-x64": "0.27.1" + "node": "^18.14.0 || >=20.6.1" } }, - "node_modules/@netlify/edge-bundler/node_modules/find-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", - "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^7.2.0", - "path-exists": "^5.0.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@netlify/edge-bundler/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@netlify/edge-functions": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@netlify/edge-functions/-/edge-functions-3.0.2.tgz", - "integrity": "sha512-1vW3R+Rc2JxL6qITndlT87N94GPjJ6gH2ntXW3IDdLzSABoU9XCHw4lRzDw+bhgSLTm0oyOwQA2+hhFvstznNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/types": "2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@netlify/edge-functions-bootstrap": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@netlify/edge-functions-bootstrap/-/edge-functions-bootstrap-2.16.0.tgz", - "integrity": "sha512-v8QQihSbBHj3JxtJsHoepXALpNumD9M7egHoc8z62FYl5it34dWczkaJoFFopEyhiBVKi4K/n0ZYpdzwfujd6g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@netlify/edge-functions-dev": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@netlify/edge-functions-dev/-/edge-functions-dev-1.0.6.tgz", - "integrity": "sha512-3T9OQVEHP/i/248K2bklrQ995dEfs0FhIiYAfzeCkeAKAB2ipbnXHqUDXMBXiQPM0Rl12U82yh+RM+LG5UFQZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/dev-utils": "4.3.2", - "@netlify/edge-bundler": "^14.9.0", - "@netlify/edge-functions": "3.0.2", - "@netlify/edge-functions-bootstrap": "2.16.0", - "@netlify/runtime-utils": "2.2.1", - "get-port": "^7.1.0" - }, - "engines": { - "node": ">=20.6.1" - } - }, - "node_modules/@netlify/functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-5.1.0.tgz", - "integrity": "sha512-LZtiQtf/QzPHIeNDZuIBxx04kmU7lCipWqZ26ejX7mYSB3yj2wvpZfF49kD8B8FoKTydSvgFmBpIcCO5FvpEXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/types": "2.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@netlify/functions-dev": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@netlify/functions-dev/-/functions-dev-1.1.3.tgz", - "integrity": "sha512-IXSmXvdBhykOMb1sOWYKAZYqAWU0WbfPfch5y+5RXe0vq6ubxzM2WiHJlyl0e14L3N2cra7dvDBh6JhbF045dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/blobs": "10.4.2", - "@netlify/dev-utils": "4.3.2", - "@netlify/functions": "5.1.0", - "@netlify/zip-it-and-ship-it": "^14.1.14", - "cron-parser": "^4.9.0", - "decache": "^4.6.2", - "extract-zip": "^2.0.1", - "is-stream": "^4.0.1", - "jwt-decode": "^4.0.0", - "lambda-local": "^2.2.0", - "read-package-up": "^11.0.0", - "semver": "^7.6.3", - "source-map-support": "^0.5.21" - }, - "engines": { - "node": ">=20.6.1" - } - }, - "node_modules/@netlify/functions-dev/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@netlify/headers": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@netlify/headers/-/headers-2.1.2.tgz", - "integrity": "sha512-3dkP1LU9U3ynKHuLUP0HCzJhf0bLs/ESuz1QjnUTOwx6oxc3hkIFa8B8wAa00uOrx3lVgcN76Zuydvkjulk7wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/headers-parser": "^9.0.2" - }, - "engines": { - "node": ">=20.6.1" - } - }, - "node_modules/@netlify/headers-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@netlify/headers-parser/-/headers-parser-9.0.2.tgz", - "integrity": "sha512-86YEGPxVemhksY1LeSr8NSOyH11RHvYHq+FuBJnTlPZoRDX+TD+0TAxF6lwzAgVTd1VPkyFEHlNgUGqw7aNzRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@iarna/toml": "^2.2.5", - "escape-string-regexp": "^5.0.0", - "fast-safe-stringify": "^2.0.7", - "is-plain-obj": "^4.0.0", - "map-obj": "^5.0.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": ">=18.14.0" - } - }, - "node_modules/@netlify/headers-parser/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/@netlify/images": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@netlify/images/-/images-1.3.2.tgz", - "integrity": "sha512-/YEhpm0KbiDtbYZbfOv6tbJyk+/j10tndeJAvUc94mSyv3EB7X4DREvhftZcxLuE6A2IceaKIbh5DpUNgJHjxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ipx": "^3.1.1" - }, - "engines": { - "node": ">=20.6.1" - } - }, - "node_modules/@netlify/open-api": { - "version": "2.45.0", - "resolved": "https://registry.npmjs.org/@netlify/open-api/-/open-api-2.45.0.tgz", - "integrity": "sha512-kLysr2N8HQi0qoEq04vpRvrE/fSnZaXJYf1bVxKre2lLaM1RSm05hqDswKTgxM601pZf9h1i1Ea3L4DZNgHb5w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.8.0" - } - }, - "node_modules/@netlify/otel": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-5.0.1.tgz", - "integrity": "sha512-h3Em98qEUQ+WGYbTxsulr5TUrNcIjP2+SpkbBfToyFLIBH3DYofvhsWajlySQoBijuRKB35OUHxt/QuX/fuo5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "1.9.0", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-trace-node": "1.30.1" - }, - "engines": { - "node": "^18.14.0 || >=20.6.1" - } - }, - "node_modules/@netlify/redirect-parser": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@netlify/redirect-parser/-/redirect-parser-15.0.3.tgz", - "integrity": "sha512-/HB3fcRRNgf6O/pbLn4EYNDHrU2kiadMMnazg8/OjvQK2S9i4y61vQcrICvDxYKUKQdgeEaABUuaCNAJFnfD9w==", + "node_modules/@netlify/redirect-parser": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@netlify/redirect-parser/-/redirect-parser-15.0.3.tgz", + "integrity": "sha512-/HB3fcRRNgf6O/pbLn4EYNDHrU2kiadMMnazg8/OjvQK2S9i4y61vQcrICvDxYKUKQdgeEaABUuaCNAJFnfD9w==", "dev": true, "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", "fast-safe-stringify": "^2.1.1", - "is-plain-obj": "^4.0.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": ">=18.14.0" - } - }, - "node_modules/@netlify/redirect-parser/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/@netlify/redirects": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@netlify/redirects/-/redirects-3.1.3.tgz", - "integrity": "sha512-gIk/h4nzg9n/LWV7odfqMP00MBRSfujnW50DRYkWqh4ApW5NdfZxOcla7ISl6hZcIDMUruOPcFOD7PFXSALVvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/dev-utils": "4.3.2", - "@netlify/redirect-parser": "^15.0.3", - "cookie": "^1.0.2", - "jsonwebtoken": "9.0.2", - "netlify-redirector": "^0.5.0" - }, - "engines": { - "node": ">=20.6.1" - } - }, - "node_modules/@netlify/runtime": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@netlify/runtime/-/runtime-4.1.10.tgz", - "integrity": "sha512-7Ilxa+7zljPkZkY7a7HHiC16LEf6rYKFnBX6QPDJ43QHmr6uxzo3m1pBj/qpnYeUZ6h0qi08/QF97I0FwKTvVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/blobs": "^10.4.2", - "@netlify/cache": "3.3.3", - "@netlify/runtime-utils": "2.2.1", - "@netlify/types": "2.2.0" - }, - "engines": { - "node": ">=20.6.1" - } - }, - "node_modules/@netlify/runtime-utils": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@netlify/runtime-utils/-/runtime-utils-2.2.1.tgz", - "integrity": "sha512-dyJeuggzQM8+Dsi0T8Z9UjfLJ6vCmNC36W6WE2aqzfTdTw4wPkh2xlEu4LoD75+TGuYK7jIhEoU2QcCXOzfyAQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || >=20" - } - }, - "node_modules/@netlify/serverless-functions-api": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-2.8.0.tgz", - "integrity": "sha512-5ZhDAZaumUAzs5hmX0NGLEsAo3XYsQKbzfOENrHf+kwGv0MdiBlIiS5rdnn51rkLVmlYd5d+6T+7J2L0bNuZxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@netlify/static": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@netlify/static/-/static-3.1.2.tgz", - "integrity": "sha512-1kxT/xTro9+zzdbyxdNSZpzwzOwlxBTbLPpVsuvF+77euman6Fxhwyt9KoEBMSwO3dyKC3LW9W4Wa/k+zeDqpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">=20.6.1" - } - }, - "node_modules/@netlify/types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@netlify/types/-/types-2.2.0.tgz", - "integrity": "sha512-XOWlZ2wPpdRKkAOcQbjIf/Qz7L4RjcSVINVNQ9p3F6U8V6KSEOsB3fPrc6Ly8EOeJioHUepRPuzHzJE/7V5EsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || >=20" - } - }, - "node_modules/@netlify/vite-plugin": { - "version": "2.7.15", - "resolved": "https://registry.npmjs.org/@netlify/vite-plugin/-/vite-plugin-2.7.15.tgz", - "integrity": "sha512-Vx4F+C31C63o3Z38bux7dUTFMfJA4ts9kkaScEn82WUDAQe/OsQgDqFYQA5WU9IKRv7LABMjzYk38Xc2/xV44g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/dev": "4.8.3", - "@netlify/dev-utils": "^4.3.2", - "dedent": "^1.7.0" - }, - "engines": { - "node": "^20.6.1 || >=22" - }, - "peerDependencies": { - "vite": "^5 || ^6 || ^7" - } - }, - "node_modules/@netlify/zip-it-and-ship-it": { - "version": "14.1.15", - "resolved": "https://registry.npmjs.org/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-14.1.15.tgz", - "integrity": "sha512-n6ZNnNOZKVFWSGx8WR4YfF7UEnUT6mSMOoyUd1oioIWasWQ3gVLFpWHSqieA1r/k0ULnxiJpTQcqiI0VLMo7Bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.22.5", - "@babel/types": "7.28.5", - "@netlify/binary-info": "^1.0.0", - "@netlify/serverless-functions-api": "^2.8.0", - "@vercel/nft": "0.29.4", - "archiver": "^7.0.0", - "common-path-prefix": "^3.0.0", - "copy-file": "^11.0.0", - "es-module-lexer": "^1.0.0", - "esbuild": "0.27.1", - "execa": "^8.0.0", - "fast-glob": "^3.3.3", - "filter-obj": "^6.0.0", - "find-up": "^7.0.0", - "is-path-inside": "^4.0.0", - "junk": "^4.0.0", - "locate-path": "^7.0.0", - "merge-options": "^3.0.4", - "minimatch": "^9.0.0", - "normalize-path": "^3.0.0", - "p-map": "^7.0.0", - "path-exists": "^5.0.0", - "precinct": "^12.0.0", - "require-package-name": "^2.0.1", - "resolve": "^2.0.0-next.1", - "semver": "^7.3.8", - "tmp-promise": "^3.0.2", - "toml": "^3.0.0", - "unixify": "^1.0.0", - "urlpattern-polyfill": "8.0.2", - "yargs": "^17.0.0", - "zod": "^3.23.8" - }, - "bin": { - "zip-it-and-ship-it": "bin.js" - }, - "engines": { - "node": ">=18.14.0" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", - "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", - "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", - "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", - "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", - "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/darwin-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", - "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", - "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", - "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", - "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", - "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", - "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-loong64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", - "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", - "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", - "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", - "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-s390x": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", - "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", - "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "is-plain-obj": "^4.0.0", + "path-exists": "^5.0.0" + }, "engines": { - "node": ">=18" + "node": ">=18.14.0" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", - "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/redirect-parser/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", - "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/redirects": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@netlify/redirects/-/redirects-3.1.3.tgz", + "integrity": "sha512-gIk/h4nzg9n/LWV7odfqMP00MBRSfujnW50DRYkWqh4ApW5NdfZxOcla7ISl6hZcIDMUruOPcFOD7PFXSALVvQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "@netlify/dev-utils": "4.3.2", + "@netlify/redirect-parser": "^15.0.3", + "cookie": "^1.0.2", + "jsonwebtoken": "9.0.2", + "netlify-redirector": "^0.5.0" + }, "engines": { - "node": ">=18" + "node": ">=20.6.1" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", - "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/runtime": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@netlify/runtime/-/runtime-4.1.9.tgz", + "integrity": "sha512-q+kg+1s7MZL3GMDY6KwsWxpFLmkk7JL3+wRrLKIrDh+CBMPBOMnGV1uLfT8ndOHf7sicvMf8xLIlv/gzZt7B8w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "dependencies": { + "@netlify/blobs": "^10.4.1", + "@netlify/cache": "3.3.3", + "@netlify/runtime-utils": "2.2.1", + "@netlify/types": "2.2.0" + }, "engines": { - "node": ">=18" + "node": ">=20.6.1" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", - "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/runtime-utils": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@netlify/runtime-utils/-/runtime-utils-2.2.1.tgz", + "integrity": "sha512-dyJeuggzQM8+Dsi0T8Z9UjfLJ6vCmNC36W6WE2aqzfTdTw4wPkh2xlEu4LoD75+TGuYK7jIhEoU2QcCXOzfyAQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], "engines": { - "node": ">=18" + "node": "^18.14.0 || >=20" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", - "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/serverless-functions-api": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-2.7.2.tgz", + "integrity": "sha512-/hevTzZMi0kZdclzfoIAd+UfXcYG/E9CjIiAqy6mFN0sSjeHdUO0v6P8GF2heVtQQzUyMBxebMmAzUVt5TsbXg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], "engines": { - "node": ">=18" + "node": ">=18.0.0" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/sunos-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", - "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/static": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@netlify/static/-/static-3.1.2.tgz", + "integrity": "sha512-1kxT/xTro9+zzdbyxdNSZpzwzOwlxBTbLPpVsuvF+77euman6Fxhwyt9KoEBMSwO3dyKC3LW9W4Wa/k+zeDqpg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], + "dependencies": { + "mime-types": "^3.0.0" + }, "engines": { - "node": ">=18" + "node": ">=20.6.1" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", - "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", - "cpu": [ - "arm64" - ], + "node_modules/@netlify/types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@netlify/types/-/types-2.2.0.tgz", + "integrity": "sha512-XOWlZ2wPpdRKkAOcQbjIf/Qz7L4RjcSVINVNQ9p3F6U8V6KSEOsB3fPrc6Ly8EOeJioHUepRPuzHzJE/7V5EsA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": ">=18" + "node": "^18.14.0 || >=20" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", - "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", - "cpu": [ - "ia32" - ], + "node_modules/@netlify/vite-plugin": { + "version": "2.7.14", + "resolved": "https://registry.npmjs.org/@netlify/vite-plugin/-/vite-plugin-2.7.14.tgz", + "integrity": "sha512-Vf9fljPa+3CVOtCFFmuAQjHEQ6WuYGHs1Gcs2nOVS626zL96Hu4vXvYztndJx6R4cO9uWD8NQPtvpHWZLdQNzA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@netlify/dev": "4.8.2", + "@netlify/dev-utils": "^4.3.2", + "dedent": "^1.7.0" + }, "engines": { - "node": ">=18" + "node": "^20.6.1 || >=22" + }, + "peerDependencies": { + "vite": "^5 || ^6 || ^7" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", - "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", - "cpu": [ - "x64" - ], + "node_modules/@netlify/zip-it-and-ship-it": { + "version": "14.1.14", + "resolved": "https://registry.npmjs.org/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-14.1.14.tgz", + "integrity": "sha512-33w50VcYLZ7RpUCFvl+n8JoLRGSVKerbH6cXtVjzA7un9JSkJWZQVS3nDmWYbq6OR0VnS1LGn7r+/ll6pSOvCg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "@babel/parser": "^7.22.5", + "@babel/types": "7.28.5", + "@netlify/binary-info": "^1.0.0", + "@netlify/serverless-functions-api": "^2.7.2", + "@vercel/nft": "0.29.4", + "archiver": "^7.0.0", + "common-path-prefix": "^3.0.0", + "copy-file": "^11.0.0", + "es-module-lexer": "^1.0.0", + "esbuild": "0.25.11", + "execa": "^8.0.0", + "fast-glob": "^3.3.3", + "filter-obj": "^6.0.0", + "find-up": "^7.0.0", + "is-path-inside": "^4.0.0", + "junk": "^4.0.0", + "locate-path": "^7.0.0", + "merge-options": "^3.0.4", + "minimatch": "^9.0.0", + "normalize-path": "^3.0.0", + "p-map": "^7.0.0", + "path-exists": "^5.0.0", + "precinct": "^12.0.0", + "require-package-name": "^2.0.1", + "resolve": "^2.0.0-next.1", + "semver": "^7.3.8", + "tmp-promise": "^3.0.2", + "toml": "^3.0.0", + "unixify": "^1.0.0", + "urlpattern-polyfill": "8.0.2", + "yargs": "^17.0.0", + "zod": "^3.23.8" + }, + "bin": { + "zip-it-and-ship-it": "bin.js" + }, "engines": { - "node": ">=18" + "node": ">=18.14.0" } }, "node_modules/@netlify/zip-it-and-ship-it/node_modules/brace-expansion": { @@ -4025,48 +3096,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/esbuild": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", - "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.1", - "@esbuild/android-arm": "0.27.1", - "@esbuild/android-arm64": "0.27.1", - "@esbuild/android-x64": "0.27.1", - "@esbuild/darwin-arm64": "0.27.1", - "@esbuild/darwin-x64": "0.27.1", - "@esbuild/freebsd-arm64": "0.27.1", - "@esbuild/freebsd-x64": "0.27.1", - "@esbuild/linux-arm": "0.27.1", - "@esbuild/linux-arm64": "0.27.1", - "@esbuild/linux-ia32": "0.27.1", - "@esbuild/linux-loong64": "0.27.1", - "@esbuild/linux-mips64el": "0.27.1", - "@esbuild/linux-ppc64": "0.27.1", - "@esbuild/linux-riscv64": "0.27.1", - "@esbuild/linux-s390x": "0.27.1", - "@esbuild/linux-x64": "0.27.1", - "@esbuild/netbsd-arm64": "0.27.1", - "@esbuild/netbsd-x64": "0.27.1", - "@esbuild/openbsd-arm64": "0.27.1", - "@esbuild/openbsd-x64": "0.27.1", - "@esbuild/openharmony-arm64": "0.27.1", - "@esbuild/sunos-x64": "0.27.1", - "@esbuild/win32-arm64": "0.27.1", - "@esbuild/win32-ia32": "0.27.1", - "@esbuild/win32-x64": "0.27.1" - } - }, "node_modules/@netlify/zip-it-and-ship-it/node_modules/find-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", @@ -4203,22 +3232,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@netlify/zip-it-and-ship-it/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -4232,7 +3250,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -4242,7 +3259,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -4282,7 +3298,6 @@ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -5606,9 +4621,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.90.12", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.12.tgz", - "integrity": "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==", + "version": "5.90.11", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.11.tgz", + "integrity": "sha512-f9z/nXhCgWDF4lHqgIE30jxLe4sYv15QodfdPDKYAk7nAEjNcndy4dHz3ezhdUaR23BpWa4I2EH4/DZ0//Uf8A==", "license": "MIT", "funding": { "type": "github", @@ -5616,12 +4631,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.90.12", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.12.tgz", - "integrity": "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==", + "version": "5.90.11", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.11.tgz", + "integrity": "sha512-3uyzz01D1fkTLXuxF3JfoJoHQMU2fxsfJwE+6N5hHy0dVNoZOvwKP8Z2k7k1KDeD54N20apcJnG75TBAStIrBA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.90.12" + "@tanstack/query-core": "5.90.11" }, "funding": { "type": "github", @@ -5737,7 +4752,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -5881,7 +4897,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -5892,7 +4907,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -5936,17 +4950,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.1.tgz", - "integrity": "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.0.tgz", + "integrity": "sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.48.1", - "@typescript-eslint/type-utils": "8.48.1", - "@typescript-eslint/utils": "8.48.1", - "@typescript-eslint/visitor-keys": "8.48.1", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/type-utils": "8.48.0", + "@typescript-eslint/utils": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -5960,7 +4974,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.48.1", + "@typescript-eslint/parser": "^8.48.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -5976,17 +4990,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.1.tgz", - "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.0.tgz", + "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.48.1", - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/typescript-estree": "8.48.1", - "@typescript-eslint/visitor-keys": "8.48.1", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4" }, "engines": { @@ -6002,14 +5015,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.1.tgz", - "integrity": "sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.0.tgz", + "integrity": "sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.48.1", - "@typescript-eslint/types": "^8.48.1", + "@typescript-eslint/tsconfig-utils": "^8.48.0", + "@typescript-eslint/types": "^8.48.0", "debug": "^4.3.4" }, "engines": { @@ -6024,14 +5037,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.1.tgz", - "integrity": "sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.0.tgz", + "integrity": "sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/visitor-keys": "8.48.1" + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6042,9 +5055,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.1.tgz", - "integrity": "sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.0.tgz", + "integrity": "sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==", "dev": true, "license": "MIT", "engines": { @@ -6059,15 +5072,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.1.tgz", - "integrity": "sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.0.tgz", + "integrity": "sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/typescript-estree": "8.48.1", - "@typescript-eslint/utils": "8.48.1", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/utils": "8.48.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -6084,9 +5097,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.1.tgz", - "integrity": "sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.0.tgz", + "integrity": "sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==", "dev": true, "license": "MIT", "engines": { @@ -6098,16 +5111,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.1.tgz", - "integrity": "sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.0.tgz", + "integrity": "sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.48.1", - "@typescript-eslint/tsconfig-utils": "8.48.1", - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/visitor-keys": "8.48.1", + "@typescript-eslint/project-service": "8.48.0", + "@typescript-eslint/tsconfig-utils": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", @@ -6165,17 +5178,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.1.tgz", - "integrity": "sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.0.tgz", + "integrity": "sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.48.1", - "@typescript-eslint/types": "8.48.1", - "@typescript-eslint/typescript-estree": "8.48.1" + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6190,13 +5202,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.1.tgz", - "integrity": "sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.0.tgz", + "integrity": "sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.1", + "@typescript-eslint/types": "8.48.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -6676,14 +5688,14 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz", - "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", + "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.25", + "@vue/shared": "3.5.24", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -6710,28 +5722,28 @@ "license": "MIT" }, "node_modules/@vue/compiler-dom": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", - "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", + "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.25", - "@vue/shared": "3.5.25" + "@vue/compiler-core": "3.5.24", + "@vue/shared": "3.5.24" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", - "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", + "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.25", - "@vue/compiler-dom": "3.5.25", - "@vue/compiler-ssr": "3.5.25", - "@vue/shared": "3.5.25", + "@vue/compiler-core": "3.5.24", + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", @@ -6746,20 +5758,20 @@ "license": "MIT" }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", - "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", + "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.25", - "@vue/shared": "3.5.25" + "@vue/compiler-dom": "3.5.24", + "@vue/shared": "3.5.24" } }, "node_modules/@vue/shared": { - "version": "3.5.25", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz", - "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", + "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", "dev": true, "license": "MIT" }, @@ -6873,7 +5885,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6917,7 +5928,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7538,7 +6548,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -8088,6 +7097,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 12.0.0" } @@ -8812,7 +7822,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dom-serializer": { "version": "2.0.0", @@ -9274,7 +8285,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -9360,7 +8370,6 @@ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -9446,7 +8455,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -9482,6 +8490,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@typescript-eslint/types": "^8.35.0", "comment-parser": "^1.4.1", @@ -9520,6 +8529,7 @@ "dev": true, "license": "ISC", "optional": true, + "peer": true, "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -9537,6 +8547,7 @@ "dev": true, "license": "ISC", "optional": true, + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -9869,7 +8880,6 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -9886,7 +8896,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -9936,7 +8945,6 @@ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -11664,7 +10672,6 @@ "integrity": "sha512-Pcfm3eZ+eO4JdZCXthW9tCDT3nF4K+9dmeZ+5X39n+Kqz0DDIABRP5CAEOHRFZk8RGuC2efksTJxrjp8EXCunQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.19", "@asamuzakjp/dom-selector": "^6.7.3", @@ -11816,13 +10823,13 @@ } }, "node_modules/jws": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", - "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "dev": true, "license": "MIT", "dependencies": { - "jwa": "^1.4.2", + "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, @@ -12349,9 +11356,9 @@ } }, "node_modules/lucide-react": { - "version": "0.556.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.556.0.tgz", - "integrity": "sha512-iOb8dRk7kLaYBZhR2VlV1CeJGxChBgUthpSP8wom9jfj79qovgG6qcSdiy6vkoREKPnbUYzJsCn4o4PtG3Iy+A==", + "version": "0.555.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.555.0.tgz", + "integrity": "sha512-D8FvHUGbxWBRQM90NZeIyhAvkFfsh3u9ekrMvJ30Z6gnpBHS6HC6ldLg7tL45hwiIz/u66eKDtdA23gwwGsAHA==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -12373,6 +11380,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -12731,7 +11739,6 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -13304,7 +12311,6 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -13471,9 +12477,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/msw": { - "version": "2.12.4", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.4.tgz", - "integrity": "sha512-rHNiVfTyKhzc0EjoXUBVGteNKBevdjOlVC6GlIRXpy+/3LHEIGRovnB5WPjcvmNODVQ1TNFnoa7wsGbd0V3epg==", + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.3.tgz", + "integrity": "sha512-/5rpGC0eK8LlFqsHaBmL19/PVKxu/CCt8pO1vzp9X6SDLsRDh/Ccudkf3Ur5lyaKxJz9ndAx+LaThdv0ySqB6A==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -13643,9 +12649,9 @@ "license": "MIT" }, "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", + "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { @@ -13665,9 +12671,9 @@ } }, "node_modules/node-mock-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", - "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.3.tgz", + "integrity": "sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==", "dev": true, "license": "MIT" }, @@ -14521,7 +13527,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -14610,6 +13615,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -14625,6 +13631,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -14635,6 +13642,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -14723,8 +13731,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/quote-unquote": { "version": "1.0.0", @@ -14741,26 +13748,24 @@ "license": "MIT" }, "node_modules/react": { - "version": "19.2.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", - "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.2.tgz", + "integrity": "sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz", - "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==", + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.2.tgz", + "integrity": "sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g==", "license": "MIT", - "peer": true, "dependencies": { - "scheduler": "^0.27.0" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^19.2.1" + "react": "^19.1.2" } }, "node_modules/react-is": { @@ -14768,7 +13773,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-markdown": { "version": "10.1.0", @@ -14808,9 +13814,9 @@ } }, "node_modules/react-router": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.1.tgz", - "integrity": "sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz", + "integrity": "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -14830,12 +13836,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.1.tgz", - "integrity": "sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.6.tgz", + "integrity": "sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==", "license": "MIT", "dependencies": { - "react-router": "7.10.1" + "react-router": "7.9.6" }, "engines": { "node": ">=20.0.0" @@ -15182,7 +14188,6 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -15194,7 +14199,6 @@ "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.7" }, @@ -15248,7 +14252,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -15381,9 +14384,9 @@ } }, "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "license": "MIT" }, "node_modules/semver": { @@ -16157,8 +15160,7 @@ "version": "4.1.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tapable": { "version": "2.2.3", @@ -16284,7 +15286,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -16599,7 +15600,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16609,16 +15609,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.48.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.1.tgz", - "integrity": "sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.0.tgz", + "integrity": "sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.48.1", - "@typescript-eslint/parser": "8.48.1", - "@typescript-eslint/typescript-estree": "8.48.1", - "@typescript-eslint/utils": "8.48.1" + "@typescript-eslint/eslint-plugin": "8.48.0", + "@typescript-eslint/parser": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/utils": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -16640,9 +15640,9 @@ "license": "MIT" }, "node_modules/ulid": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/ulid/-/ulid-3.0.2.tgz", - "integrity": "sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-3.0.1.tgz", + "integrity": "sha512-dPJyqPzx8preQhqq24bBG1YNkvigm87K8kVEHCD+ruZg24t6IFEFv00xMWfxcC4djmFtiTLdFuADn4+DOz6R7Q==", "dev": true, "license": "MIT", "bin": { @@ -17141,12 +16141,11 @@ } }, "node_modules/vite": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.6.tgz", - "integrity": "sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==", + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", + "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -17282,7 +16281,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -18016,12 +17014,11 @@ } }, "node_modules/zod": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", - "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/web/package.json b/web/package.json index b45d058e6c..133822dc60 100644 --- a/web/package.json +++ b/web/package.json @@ -22,20 +22,20 @@ "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.17", "@tailwindcss/typography": "^0.5.19", - "@tanstack/react-query": "^5.90.12", - "lucide-react": "^0.556.0", + "@tanstack/react-query": "^5.90.11", + "lucide-react": "^0.555.0", "openapi-fetch": "^0.15.0", "react": "^19.1.2", - "react-dom": "^19.2.1", + "react-dom": "^19.1.2", "react-markdown": "^10.1.0", - "react-router-dom": "^7.10.1", + "react-router-dom": "^7.9.6", "remark-gfm": "^4.0.1" }, "devDependencies": { "@eslint/js": "^9.39.1", "@faker-js/faker": "^10.1.0", "@netlify/functions": "^5.0.1", - "@netlify/vite-plugin": "^2.7.15", + "@netlify/vite-plugin": "^2.7.14", "@tailwindcss/vite": "^4.1.17", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", @@ -52,14 +52,14 @@ "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", "jsdom": "^27.1.0", - "msw": "2.12.4", + "msw": "2.12.3", "openapi-backend": "^5.15.0", "openapi-typescript": "^7.10.1", "tailwindcss": "^4.1.13", "typescript": "^5.9.3", - "typescript-eslint": "^8.48.1", + "typescript-eslint": "^8.48.0", "unified": "^11.0.5", - "vite": "^7.2.6", + "vite": "^7.2.4", "vite-plugin-static-copy": "^3.1.4", "vitest": "^3.2.4" } diff --git a/web/src/types/api.ts b/web/src/types/api.ts index babcfdd34b..5ac93cbe06 100644 --- a/web/src/types/api.ts +++ b/web/src/types/api.ts @@ -998,6 +998,11 @@ export interface components { logs: string; status: components["schemas"]["types.Status"]; }; + "types.AppComponent": { + /** @description Chart name */ + name: string; + status: components["schemas"]["types.Status"]; + }; "types.AppConfig": { groups: components["schemas"]["v1beta1.ConfigGroup"][]; }; @@ -1016,6 +1021,7 @@ export interface components { values: components["schemas"]["types.AppConfigValues"]; }; "types.AppInstall": { + components: components["schemas"]["types.AppComponent"][]; logs: string; status: components["schemas"]["types.Status"]; }; @@ -1159,6 +1165,7 @@ export interface components { * @enum {string} */ "types.State": "Pending" | "Running" | "Succeeded" | "Failed"; + /** @description Uses existing Status type */ "types.Status": { description: string; lastUpdated: string; From 753e2fdf5cf5468430cfd57f86e22634e02cee06 Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Mon, 8 Dec 2025 11:15:25 -0800 Subject: [PATCH 02/11] f --- api/controllers/app/install.go | 2 +- api/internal/managers/app/install/install.go | 88 +++- .../managers/app/install/install_test.go | 408 +++++++++++++++++- api/internal/managers/app/install/manager.go | 2 +- api/internal/managers/app/install/mock.go | 4 +- 5 files changed, 491 insertions(+), 13 deletions(-) diff --git a/api/controllers/app/install.go b/api/controllers/app/install.go index 5ab27d256d..e1d3b5408b 100644 --- a/api/controllers/app/install.go +++ b/api/controllers/app/install.go @@ -112,7 +112,7 @@ func (c *AppController) InstallApp(ctx context.Context, opts InstallAppOptions) } // Install the app with installable charts - err = c.appInstallManager.Install(ctx, installableCharts, opts.RegistrySettings, opts.HostCABundlePath) + err = c.appInstallManager.Install(ctx, installableCharts, appConfigValues, opts.RegistrySettings, opts.HostCABundlePath) if err != nil { return fmt.Errorf("install app: %w", err) } diff --git a/api/internal/managers/app/install/install.go b/api/internal/managers/app/install/install.go index 9d75b0e967..cb63108583 100644 --- a/api/internal/managers/app/install/install.go +++ b/api/internal/managers/app/install/install.go @@ -9,10 +9,16 @@ import ( "github.com/replicatedhq/embedded-cluster/api/types" "github.com/replicatedhq/embedded-cluster/pkg/helm" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" + kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + kyaml "sigs.k8s.io/yaml" ) // Install installs the app with the provided Helm charts -func (m *appInstallManager) Install(ctx context.Context, installableCharts []types.InstallableHelmChart, registrySettings *types.RegistrySettings, hostCABundlePath string) error { +func (m *appInstallManager) Install(ctx context.Context, installableCharts []types.InstallableHelmChart, configValues types.AppConfigValues, registrySettings *types.RegistrySettings, hostCABundlePath string) error { if err := m.setupClients(); err != nil { return fmt.Errorf("setup clients: %w", err) } @@ -29,6 +35,11 @@ func (m *appInstallManager) Install(ctx context.Context, installableCharts []typ return fmt.Errorf("get kotsadm namespace: %w", err) } + // Create or update secret with config values before installing + if err := m.createConfigValuesSecret(ctx, configValues, kotsadmNamespace); err != nil { + return fmt.Errorf("creating config values secret: %w", err) + } + // Initialize components for tracking if err := m.initializeComponents(installableCharts); err != nil { return fmt.Errorf("initialize components: %w", err) @@ -42,11 +53,84 @@ func (m *appInstallManager) Install(ctx context.Context, installableCharts []typ return nil } +// createConfigValuesSecret creates or updates a Kubernetes secret with the config values. +// TODO: Handle 1MB size limitation by storing large file data fields as pointers to other secrets +// TODO: Consider maintaining history of config values for potential rollbacks +func (m *appInstallManager) createConfigValuesSecret(ctx context.Context, configValues types.AppConfigValues, namespace string) error { + // Get app slug and version from release data + license := &kotsv1beta1.License{} + if err := kyaml.Unmarshal(m.license, license); err != nil { + return fmt.Errorf("parse license: %w", err) + } + + if m.releaseData == nil || m.releaseData.ChannelRelease == nil { + return fmt.Errorf("release data is required for secret creation") + } + + // Marshal config values to YAML + data, err := kyaml.Marshal(configValues) + if err != nil { + return fmt.Errorf("marshal config values: %w", err) + } + + secretName := fmt.Sprintf("%s-config-values", license.Spec.AppSlug) + + // Create secret object + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/name": license.Spec.AppSlug, + "app.kubernetes.io/version": m.releaseData.ChannelRelease.VersionLabel, + "app.kubernetes.io/component": "config", + "app.kubernetes.io/part-of": "embedded-cluster", + "app.kubernetes.io/managed-by": "embedded-cluster-installer", + }, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "config-values.yaml": data, + }, + } + + // Try to create the secret + if err := m.kcli.Create(ctx, secret); err != nil { + if !apierrors.IsAlreadyExists(err) { + return fmt.Errorf("create config values secret: %w", err) + } + + // Secret exists, get and update it + existingSecret := &corev1.Secret{} + if err := m.kcli.Get(ctx, client.ObjectKey{ + Name: secretName, + Namespace: namespace, + }, existingSecret); err != nil { + return fmt.Errorf("get existing config values secret: %w", err) + } + + // Update the existing secret's data and labels + existingSecret.Data = secret.Data + existingSecret.Labels = secret.Labels + + if err := m.kcli.Update(ctx, existingSecret); err != nil { + return fmt.Errorf("update config values secret: %w", err) + } + } + + return nil +} + func (m *appInstallManager) installHelmCharts(ctx context.Context, installableCharts []types.InstallableHelmChart, kotsadmNamespace string) error { logFn := m.logFn("app") if len(installableCharts) == 0 { - return fmt.Errorf("no helm charts found") + logFn("no helm charts to install") + return nil } logFn("installing %d helm charts", len(installableCharts)) diff --git a/api/internal/managers/app/install/install_test.go b/api/internal/managers/app/install/install_test.go index 4f4c2714dd..8e5177b4f0 100644 --- a/api/internal/managers/app/install/install_test.go +++ b/api/internal/managers/app/install/install_test.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "compress/gzip" + "context" "encoding/base64" "errors" "fmt" @@ -23,10 +24,12 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" metadatafake "k8s.io/client-go/metadata/fake" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" kyaml "sigs.k8s.io/yaml" ) @@ -75,6 +78,12 @@ func TestAppInstallManager_Install(t *testing.T) { fakeKcli := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(kotsadmNamespace).Build() t.Run("Success", func(t *testing.T) { + configValues := types.AppConfigValues{ + "key1": {Value: "value1"}, + "key2": {Value: "value2"}, + "key3": {Value: "value3"}, + } + // Create InstallableHelmCharts with weights - should already be sorted at this stage installableCharts := []types.InstallableHelmChart{ createTestInstallableHelmChart(t, "nginx-chart", "1.0.0", "web-server", "web", 10, map[string]any{ @@ -164,7 +173,7 @@ func TestAppInstallManager_Install(t *testing.T) { require.NoError(t, err) // Run installation with registry settings and host CA bundle path - err = manager.Install(t.Context(), installableCharts, registrySettings, tmpCAFile.Name()) + err = manager.Install(t.Context(), installableCharts, configValues, registrySettings, tmpCAFile.Name()) require.NoError(t, err) mockHelmClient.AssertExpectations(t) @@ -190,6 +199,10 @@ func TestAppInstallManager_Install(t *testing.T) { }) t.Run("Install updates status correctly", func(t *testing.T) { + configValues := types.AppConfigValues{ + "key1": {Value: "value1"}, + } + installableCharts := []types.InstallableHelmChart{ createTestInstallableHelmChart(t, "monitoring-chart", "1.0.0", "prometheus", "monitoring", 0, map[string]any{"key": "value"}), } @@ -221,7 +234,7 @@ func TestAppInstallManager_Install(t *testing.T) { assert.Equal(t, types.StatePending, appInstall.Status.State) // Run installation - err = manager.Install(t.Context(), installableCharts, nil, "") + err = manager.Install(t.Context(), installableCharts, configValues, nil, "") require.NoError(t, err) // Verify components status @@ -233,6 +246,10 @@ func TestAppInstallManager_Install(t *testing.T) { }) t.Run("Install handles errors correctly", func(t *testing.T) { + configValues := types.AppConfigValues{ + "key1": {Value: "value1"}, + } + installableCharts := []types.InstallableHelmChart{ createTestInstallableHelmChart(t, "logging-chart", "1.0.0", "fluentd", "logging", 0, map[string]any{"key": "value"}), } @@ -259,7 +276,7 @@ func TestAppInstallManager_Install(t *testing.T) { require.NoError(t, err) // Run installation (should fail) - err = manager.Install(t.Context(), installableCharts, nil, "") + err = manager.Install(t.Context(), installableCharts, configValues, nil, "") assert.Error(t, err) mockHelmClient.AssertExpectations(t) @@ -386,6 +403,12 @@ func TestComponentStatusTracking(t *testing.T) { fakeKcli := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(kotsadmNamespace).Build() t.Run("Components are registered and status is tracked", func(t *testing.T) { + configValues := types.AppConfigValues{ + "key1": {Value: "value1"}, + "key2": {Value: "value2"}, + "key3": {Value: "value3"}, + } + // Create test charts with different weights installableCharts := []types.InstallableHelmChart{ createTestInstallableHelmChart(t, "database-chart", "1.0.0", "postgres", "data", 10, map[string]any{"key": "value1"}), @@ -411,7 +434,11 @@ func TestComponentStatusTracking(t *testing.T) { })) manager, err := NewAppInstallManager( WithAppInstallStore(appInstallStore), - WithReleaseData(&release.ReleaseData{}), + WithReleaseData(&release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + VersionLabel: "v1.0.0", + }, + }), WithLicense(licenseBytes), WithClusterID("test-cluster"), WithHelmClient(mockHelmClient), @@ -420,7 +447,7 @@ func TestComponentStatusTracking(t *testing.T) { require.NoError(t, err) // Install the charts - err = manager.Install(t.Context(), installableCharts, nil, "") + err = manager.Install(t.Context(), installableCharts, configValues, nil, "") require.NoError(t, err) // Verify that components were registered and have correct status @@ -441,6 +468,10 @@ func TestComponentStatusTracking(t *testing.T) { }) t.Run("Component failure is tracked correctly", func(t *testing.T) { + configValues := types.AppConfigValues{ + "key1": {Value: "value1"}, + } + // Create test chart installableCharts := []types.InstallableHelmChart{ createTestInstallableHelmChart(t, "failing-chart", "1.0.0", "failing-app", "default", 0, map[string]any{"key": "value"}), @@ -458,7 +489,11 @@ func TestComponentStatusTracking(t *testing.T) { })) manager, err := NewAppInstallManager( WithAppInstallStore(appInstallStore), - WithReleaseData(&release.ReleaseData{}), + WithReleaseData(&release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + VersionLabel: "v1.0.0", + }, + }), WithLicense(licenseBytes), WithClusterID("test-cluster"), WithHelmClient(mockHelmClient), @@ -467,7 +502,7 @@ func TestComponentStatusTracking(t *testing.T) { require.NoError(t, err) // Install the charts (should fail) - err = manager.Install(t.Context(), installableCharts, nil, "") + err = manager.Install(t.Context(), installableCharts, configValues, nil, "") require.Error(t, err) // Verify that component failure is tracked @@ -486,3 +521,362 @@ func TestComponentStatusTracking(t *testing.T) { mockHelmClient.AssertExpectations(t) }) } + +func TestAppInstallManager_Install_ConfigValuesSecret(t *testing.T) { + // Set up environment and release data for all tests + t.Setenv("ENABLE_V3", "1") + err := release.SetReleaseDataForTests(map[string][]byte{ + "channelrelease.yaml": []byte("# channel release object\nappSlug: test-app"), + }) + require.NoError(t, err) + + tests := []struct { + name string + releaseData *release.ReleaseData + configValues types.AppConfigValues + setupClient func(t *testing.T) client.Client + expectError bool + expectedErrorContains string + validateSecret func(t *testing.T, kcli client.Client) + }{ + { + name: "first install creates secret with multiple config values", + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + VersionLabel: "v1.0.0", + DefaultDomains: release.Domains{ + ReplicatedAppDomain: "replicated.app", + }, + }, + }, + configValues: types.AppConfigValues{ + "key1": {Value: "value1"}, + "key2": {Value: "value2"}, + "key3": {Value: "value3"}, + }, + setupClient: func(t *testing.T) client.Client { + sch := runtime.NewScheme() + require.NoError(t, corev1.AddToScheme(sch)) + require.NoError(t, scheme.AddToScheme(sch)) + return fake.NewClientBuilder().WithScheme(sch).Build() + }, + expectError: false, + validateSecret: func(t *testing.T, kcli client.Client) { + // Get and verify secret + secret := &corev1.Secret{} + err := kcli.Get(t.Context(), client.ObjectKey{ + Name: "test-app-config-values", + Namespace: "test-app", + }, secret) + require.NoError(t, err) + + // Verify labels + assert.Equal(t, "test-app", secret.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "v1.0.0", secret.Labels["app.kubernetes.io/version"]) + assert.Equal(t, "config", secret.Labels["app.kubernetes.io/component"]) + assert.Equal(t, "embedded-cluster", secret.Labels["app.kubernetes.io/part-of"]) + assert.Equal(t, "embedded-cluster-installer", secret.Labels["app.kubernetes.io/managed-by"]) + + // Verify type + assert.Equal(t, corev1.SecretTypeOpaque, secret.Type) + + // Verify data + data, ok := secret.Data["config-values.yaml"] + require.True(t, ok) + + // Unmarshal and verify values + var cv types.AppConfigValues + err = kyaml.Unmarshal(data, &cv) + require.NoError(t, err) + assert.Equal(t, "value1", cv["key1"].Value) + assert.Equal(t, "value2", cv["key2"].Value) + assert.Equal(t, "value3", cv["key3"].Value) + }, + }, + { + name: "existing secret is fetched and updated", + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + VersionLabel: "v1.0.0", + DefaultDomains: release.Domains{ + ReplicatedAppDomain: "replicated.app", + }, + }, + }, + configValues: types.AppConfigValues{ + "newkey": {Value: "newvalue"}, + }, + setupClient: func(t *testing.T) client.Client { + sch := runtime.NewScheme() + require.NoError(t, corev1.AddToScheme(sch)) + require.NoError(t, scheme.AddToScheme(sch)) + + // Create existing secret with old version + existingSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app-config-values", + Namespace: "test-app", + Labels: map[string]string{ + "app.kubernetes.io/version": "v0.9.0", + }, + }, + Data: map[string][]byte{ + "config-values.yaml": []byte("old: data"), + }, + } + + return fake.NewClientBuilder(). + WithScheme(sch). + WithObjects(existingSecret). + Build() + }, + expectError: false, + validateSecret: func(t *testing.T, kcli client.Client) { + // Get and verify secret was recreated + secret := &corev1.Secret{} + err := kcli.Get(t.Context(), client.ObjectKey{ + Name: "test-app-config-values", + Namespace: "test-app", + }, secret) + require.NoError(t, err) + + // Verify updated version label + assert.Equal(t, "v1.0.0", secret.Labels["app.kubernetes.io/version"]) + + // Verify new data + data, ok := secret.Data["config-values.yaml"] + require.True(t, ok) + + var cv types.AppConfigValues + err = kyaml.Unmarshal(data, &cv) + require.NoError(t, err) + assert.Equal(t, "newvalue", cv["newkey"].Value) + }, + }, + { + name: "fails when release data is missing", + releaseData: nil, + configValues: types.AppConfigValues{ + "key1": {Value: "value1"}, + }, + setupClient: func(t *testing.T) client.Client { + sch := runtime.NewScheme() + require.NoError(t, corev1.AddToScheme(sch)) + require.NoError(t, scheme.AddToScheme(sch)) + return fake.NewClientBuilder().WithScheme(sch).Build() + }, + expectError: true, + expectedErrorContains: "release data is required", + }, + { + name: "fails when channel release is missing", + releaseData: &release.ReleaseData{ + ChannelRelease: nil, + }, + configValues: types.AppConfigValues{ + "key1": {Value: "value1"}, + }, + setupClient: func(t *testing.T) client.Client { + sch := runtime.NewScheme() + require.NoError(t, corev1.AddToScheme(sch)) + require.NoError(t, scheme.AddToScheme(sch)) + return fake.NewClientBuilder().WithScheme(sch).Build() + }, + expectError: true, + expectedErrorContains: "release data is required", + }, + { + name: "fails when get returns error", + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + VersionLabel: "v1.0.0", + DefaultDomains: release.Domains{ + ReplicatedAppDomain: "replicated.app", + }, + }, + }, + configValues: types.AppConfigValues{ + "key1": {Value: "value1"}, + }, + setupClient: func(t *testing.T) client.Client { + sch := runtime.NewScheme() + require.NoError(t, corev1.AddToScheme(sch)) + require.NoError(t, scheme.AddToScheme(sch)) + + // Create existing secret + existingSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app-config-values", + Namespace: "test-app", + }, + } + + return fake.NewClientBuilder(). + WithScheme(sch). + WithObjects(existingSecret). + WithInterceptorFuncs(interceptor.Funcs{ + Get: func(ctx context.Context, c client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + if key.Name == "test-app-config-values" { + return fmt.Errorf("simulated get error") + } + return c.Get(ctx, key, obj, opts...) + }, + }). + Build() + }, + expectError: true, + expectedErrorContains: "get existing config values secret", + }, + { + name: "fails when update returns error", + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + VersionLabel: "v1.0.0", + DefaultDomains: release.Domains{ + ReplicatedAppDomain: "replicated.app", + }, + }, + }, + configValues: types.AppConfigValues{ + "key1": {Value: "value1"}, + }, + setupClient: func(t *testing.T) client.Client { + sch := runtime.NewScheme() + require.NoError(t, corev1.AddToScheme(sch)) + require.NoError(t, scheme.AddToScheme(sch)) + + // Create existing secret + existingSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app-config-values", + Namespace: "test-app", + }, + } + + return fake.NewClientBuilder(). + WithScheme(sch). + WithObjects(existingSecret). + WithInterceptorFuncs(interceptor.Funcs{ + Update: func(ctx context.Context, c client.WithWatch, obj client.Object, opts ...client.UpdateOption) error { + if secret, ok := obj.(*corev1.Secret); ok && secret.Name == "test-app-config-values" { + return fmt.Errorf("simulated update error") + } + return c.Update(ctx, obj, opts...) + }, + }). + Build() + }, + expectError: true, + expectedErrorContains: "update config values secret", + }, + { + name: "fails when create returns non-AlreadyExists error", + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + VersionLabel: "v1.0.0", + DefaultDomains: release.Domains{ + ReplicatedAppDomain: "replicated.app", + }, + }, + }, + configValues: types.AppConfigValues{ + "key1": {Value: "value1"}, + }, + setupClient: func(t *testing.T) client.Client { + sch := runtime.NewScheme() + require.NoError(t, corev1.AddToScheme(sch)) + require.NoError(t, scheme.AddToScheme(sch)) + + return fake.NewClientBuilder(). + WithScheme(sch). + WithInterceptorFuncs(interceptor.Funcs{ + Create: func(ctx context.Context, c client.WithWatch, obj client.Object, opts ...client.CreateOption) error { + if _, ok := obj.(*corev1.Secret); ok { + return fmt.Errorf("simulated create error") + } + return c.Create(ctx, obj, opts...) + }, + }). + Build() + }, + expectError: true, + expectedErrorContains: "create config values secret", + }, + { + name: "handles empty config values", + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + VersionLabel: "v1.0.0", + DefaultDomains: release.Domains{ + ReplicatedAppDomain: "replicated.app", + }, + }, + }, + configValues: types.AppConfigValues{ + "key1": {Value: "value1"}, + }, + setupClient: func(t *testing.T) client.Client { + sch := runtime.NewScheme() + require.NoError(t, corev1.AddToScheme(sch)) + require.NoError(t, scheme.AddToScheme(sch)) + return fake.NewClientBuilder().WithScheme(sch).Build() + }, + expectError: false, + validateSecret: func(t *testing.T, kcli client.Client) { + // Get and verify secret was created even with empty values + secret := &corev1.Secret{} + err := kcli.Get(t.Context(), client.ObjectKey{ + Name: "test-app-config-values", + Namespace: "test-app", + }, secret) + require.NoError(t, err) + + // Verify data exists + data, ok := secret.Data["config-values.yaml"] + require.True(t, ok) + require.NotEmpty(t, data) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Setup + license := &kotsv1beta1.License{ + Spec: kotsv1beta1.LicenseSpec{AppSlug: "test-app"}, + } + licenseBytes, err := kyaml.Marshal(license) + require.NoError(t, err) + + kcli := tt.setupClient(t) + + // Create mock helm client + mockHelmClient := &helm.MockClient{} + + manager, err := NewAppInstallManager( + WithLicense(licenseBytes), + WithClusterID("test-cluster"), + WithAirgapBundle("test-airgap.tar.gz"), + WithReleaseData(tt.releaseData), + WithLogger(logger.NewDiscardLogger()), + WithKubeClient(kcli), + WithHelmClient(mockHelmClient), + ) + require.NoError(t, err) + + // Execute + err = manager.Install(t.Context(), nil, tt.configValues, nil, "") + + // Verify + if tt.expectError { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedErrorContains) + } else { + require.NoError(t, err) + if tt.validateSecret != nil { + tt.validateSecret(t, kcli) + } + } + }) + } +} diff --git a/api/internal/managers/app/install/manager.go b/api/internal/managers/app/install/manager.go index 462e8e2e77..9805df2e8d 100644 --- a/api/internal/managers/app/install/manager.go +++ b/api/internal/managers/app/install/manager.go @@ -19,7 +19,7 @@ var _ AppInstallManager = &appInstallManager{} // AppInstallManager provides methods for managing app installation type AppInstallManager interface { // Install installs the app with the provided Helm charts - Install(ctx context.Context, installableCharts []types.InstallableHelmChart, registrySettings *types.RegistrySettings, hostCABundlePath string) error + Install(ctx context.Context, installableCharts []types.InstallableHelmChart, configValues types.AppConfigValues, registrySettings *types.RegistrySettings, hostCABundlePath string) error } // appInstallManager is an implementation of the AppInstallManager interface diff --git a/api/internal/managers/app/install/mock.go b/api/internal/managers/app/install/mock.go index 1b4b95d037..100a6fa50a 100644 --- a/api/internal/managers/app/install/mock.go +++ b/api/internal/managers/app/install/mock.go @@ -13,7 +13,7 @@ type MockAppInstallManager struct { } // Install mocks the Install method -func (m *MockAppInstallManager) Install(ctx context.Context, installableCharts []types.InstallableHelmChart, registrySettings *types.RegistrySettings, hostCABundlePath string) error { - args := m.Called(ctx, installableCharts, registrySettings, hostCABundlePath) +func (m *MockAppInstallManager) Install(ctx context.Context, installableCharts []types.InstallableHelmChart, configValues types.AppConfigValues, registrySettings *types.RegistrySettings, hostCABundlePath string) error { + args := m.Called(ctx, installableCharts, configValues, registrySettings, hostCABundlePath) return args.Error(0) } From 34460297e2cdc277149cdda0a0f4ee9621a2bc77 Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Mon, 8 Dec 2025 11:33:39 -0800 Subject: [PATCH 03/11] f --- .github/workflows/dependencies.yaml | 2 +- README.md | 32 ++++++++++---------- api/controllers/app/tests/test_suite.go | 8 ++--- pkg/addons/adminconsole/static/metadata.yaml | 18 +++++------ pkg/addons/adminconsole/values.go | 3 +- pkg/addons/adminconsole/values_test.go | 3 +- pkg/addons/openebs/static/metadata.yaml | 4 +-- pkg/addons/velero/static/metadata.yaml | 4 +-- tests/dryrun/v3_install_test.go | 11 ++++--- versions.mk | 4 +-- 10 files changed, 46 insertions(+), 43 deletions(-) diff --git a/.github/workflows/dependencies.yaml b/.github/workflows/dependencies.yaml index 0704de4148..e8a6db0e99 100644 --- a/.github/workflows/dependencies.yaml +++ b/.github/workflows/dependencies.yaml @@ -45,7 +45,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - version=$(gh release list --repo helm/helm --json tagName,isLatest | jq -r '.[] | select(.isLatest) | .tagName') + version=$(gh release list --repo helm/helm --json tagName,createdAt | jq -r '.[] | select(.tagName | startswith("v3.")) | .tagName' | head -1) echo "helm version: $version" sed -i "/^HELM_VERSION/c\HELM_VERSION = $version" versions.mk diff --git a/README.md b/README.md index 6aa3ef7af0..cfb8ede70d 100644 --- a/README.md +++ b/README.md @@ -476,32 +476,32 @@ dagger call with-one-password --service-account=env:OP_SERVICE_ACCOUNT_TOKEN \ ## Releasing -Embedded Cluster maintains support for the current and two previous K0s minor versions, ensuring backward compatibility while supporting the latest features. -All supported versions are released simultaneously from the main branch using a structured tagging approach that combines the application version with the supported K0s version. +Embedded Cluster maintains support for the current and two previous k8s minor versions, ensuring backward compatibility while supporting the latest features. +All supported versions are released simultaneously from the main branch using a structured tagging approach that combines the application version with the supported k8s version. ### Release Tagging Strategy -Releases follow the format: `{APP_VERSION}+k0s-{K0S_MINOR_VERSION}` +Releases follow the format: `{APP_VERSION}+k8s-{K0S_MINOR_VERSION}` **Examples:** -- `2.10.0+k0s-1.33` - Application version 2.10.0 with K0s 1.33.x support -- `2.10.0+k0s-1.32` - Application version 2.10.0 with K0s 1.32.x support -- `2.10.0+k0s-1.31` - Application version 2.10.0 with K0s 1.31.x support +- `2.10.0+k8s-1.33` - Application version 2.10.0 with k8s 1.33.x support +- `2.10.0+k8s-1.32` - Application version 2.10.0 with k8s 1.32.x support +- `2.10.0+k8s-1.31` - Application version 2.10.0 with k8s 1.31.x support ### Release Process 1. **Prepare the release commit** - Ensure all changes are committed and tested -2. **Create annotated tags** - Tag the same commit with all supported K0s minor versions using annotated tags with descriptive messages: +2. **Create annotated tags** - Tag the same commit with all supported k8s minor versions using annotated tags with descriptive messages: ```bash - # Tag for K0s 1.33.x support - git tag -a 2.10.0+k0s-1.33 -m "Release 2.10.0+k0s-1.33" - git push origin 2.10.0+k0s-1.33 + # Tag for k8s 1.33.x support + git tag -a 2.10.0+k8s-1.33 -m "Release 2.10.0+k8s-1.33" + git push origin 2.10.0+k8s-1.33 - # Tag for K0s 1.32.x support - git tag -a 2.10.0+k0s-1.32 -m "Release 2.10.0+k0s-1.32" - git push origin 2.10.0+k0s-1.32 + # Tag for k8s 1.32.x support + git tag -a 2.10.0+k8s-1.32 -m "Release 2.10.0+k8s-1.32" + git push origin 2.10.0+k8s-1.32 - # Tag for K0s 1.31.x support - git tag -a 2.10.0+k0s-1.31 -m "Release 2.10.0+k0s-1.31" - git push origin 2.10.0+k0s-1.31 + # Tag for k8s 1.31.x support + git tag -a 2.10.0+k8s-1.31 -m "Release 2.10.0+k8s-1.31" + git push origin 2.10.0+k8s-1.31 ``` diff --git a/api/controllers/app/tests/test_suite.go b/api/controllers/app/tests/test_suite.go index bf3c3099cc..d5c1145883 100644 --- a/api/controllers/app/tests/test_suite.go +++ b/api/controllers/app/tests/test_suite.go @@ -517,7 +517,7 @@ func (s *AppControllerTestSuite) TestInstallApp() { return status.State == types.StateRunning })).Return(nil), - aim.On("Install", mock.Anything, expectedCharts, mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), + aim.On("Install", mock.Anything, expectedCharts, mock.AnythingOfType("types.AppConfigValues"), mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateSucceeded @@ -542,7 +542,7 @@ func (s *AppControllerTestSuite) TestInstallApp() { return status.State == types.StateRunning })).Return(nil), - aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), + aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("types.AppConfigValues"), mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateSucceeded @@ -567,7 +567,7 @@ func (s *AppControllerTestSuite) TestInstallApp() { return status.State == types.StateRunning })).Return(nil), - aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(errors.New("install error")), + aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("types.AppConfigValues"), mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(errors.New("install error")), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateFailed && strings.Contains(status.Description, "install error") @@ -615,7 +615,7 @@ func (s *AppControllerTestSuite) TestInstallApp() { return status.State == types.StateRunning })).Return(nil), - aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), + aim.On("Install", mock.Anything, []types.InstallableHelmChart{}, mock.AnythingOfType("types.AppConfigValues"), mock.AnythingOfType("*types.RegistrySettings"), mock.AnythingOfType("string")).Return(nil), store.AppInstallMockStore.On("SetStatus", mock.MatchedBy(func(status types.Status) bool { return status.State == types.StateSucceeded diff --git a/pkg/addons/adminconsole/static/metadata.yaml b/pkg/addons/adminconsole/static/metadata.yaml index dcaeb52ce3..999ab370f2 100644 --- a/pkg/addons/adminconsole/static/metadata.yaml +++ b/pkg/addons/adminconsole/static/metadata.yaml @@ -5,26 +5,26 @@ # $ make buildtools # $ output/bin/buildtools update addon # -version: 1.128.3 +version: 1.129.1 location: oci://proxy.replicated.com/library/admin-console images: kotsadm: repo: proxy.replicated.com/anonymous/kotsadm/kotsadm tag: - amd64: v1.128.3-amd64@sha256:a54b6bcc2623d8712df92f19cfd290c9b791cfd71c20ba7962aed6c5442754c8 - arm64: v1.128.3-arm64@sha256:646ae8d1987064e047f06de39ae6800f4ba617b1fec2ccb4a10b6a2a4623f39f + amd64: v1.129.1-amd64@sha256:754079c15e62752a8d3e95f969979f90c804e6ba360c4a449a8064ef0f429f9e + arm64: v1.129.1-arm64@sha256:a8cc3a8b43fcede48c12350f29c421538170adde48d9be9a4ad96ea4b3007e22 kotsadm-migrations: repo: proxy.replicated.com/anonymous/kotsadm/kotsadm-migrations tag: - amd64: v1.128.3-amd64@sha256:d4a66c0d05216e06eb30be8bcf1d541cf9922e9985e8d3dddc10a6a230d1e913 - arm64: v1.128.3-arm64@sha256:ee54465951a3ca488d9ea1539261a59e46bec98bfa2084f98c6155614fc0a084 + amd64: v1.129.1-amd64@sha256:173431dea760c2a0ba79b25b7c4d983bdfc97a25fc0ef17037bdd3eb231f6407 + arm64: v1.129.1-arm64@sha256:51ad1d1a24fca5b4b3905321f10379f246f6ded2a99807f4a02eb1bd6eb38629 kurl-proxy: repo: proxy.replicated.com/anonymous/kotsadm/kurl-proxy tag: - amd64: v1.128.3-amd64@sha256:37a442ffdd1cff6b1d5909036a6e482356f74055a826e345e213b869f0c492d5 - arm64: v1.128.3-arm64@sha256:f743cabd0bea24e50e5b7878f94150190390b301f574098fb7bb09062c11b2d9 + amd64: v1.129.1-amd64@sha256:41e6e77919110366aaf0ae5966a065d5f3506ff32232c4f5959dad576605b9eb + arm64: v1.129.1-arm64@sha256:eec1fa1123c540b56b38575fef0b224ebfbb68a737265158b64a70c8f6154655 rqlite: repo: proxy.replicated.com/anonymous/kotsadm/rqlite tag: - amd64: 9.1.3-r0-amd64@sha256:b156612409debdee543329e6b0736d29b7d57fe0946b8b202014426bc3a4c0de - arm64: 9.1.3-r0-arm64@sha256:e82e67661ae3fdcef06d3fda8a02362d7906edd7e8ed7a1941a5c382e128cf6b + amd64: 9.3.4-amd64@sha256:d67af54f63cc1fdab0e68e2d6de34dfd7a99c72f6df9d890c348198201904db9 + arm64: 9.3.4-arm64@sha256:ab44603a54dd821dc87ab4ff0c03e52dc527aa6dc760034a144baec496b1e118 diff --git a/pkg/addons/adminconsole/values.go b/pkg/addons/adminconsole/values.go index 87029ccd84..66d10f5461 100644 --- a/pkg/addons/adminconsole/values.go +++ b/pkg/addons/adminconsole/values.go @@ -45,7 +45,8 @@ func (a *AdminConsole) GenerateHelmValues(ctx context.Context, kcli client.Clien copiedValues["embeddedClusterID"] = a.ClusterID copiedValues["embeddedClusterDataDir"] = a.DataDir copiedValues["embeddedClusterK0sDir"] = a.K0sDataDir - copiedValues["isEmbeddedClusterV3"] = a.isV3() + // TODO: enable this once we stop relying on KOTS to deploy the app + // copiedValues["isEmbeddedClusterV3"] = a.isV3() } copiedValues["isHA"] = a.IsHA diff --git a/pkg/addons/adminconsole/values_test.go b/pkg/addons/adminconsole/values_test.go index 51bbd09058..0538381ab3 100644 --- a/pkg/addons/adminconsole/values_test.go +++ b/pkg/addons/adminconsole/values_test.go @@ -112,7 +112,8 @@ func TestGenerateHelmValues_Target(t *testing.T) { assert.Equal(t, "123", values["embeddedClusterID"]) assert.Equal(t, dataDir, values["embeddedClusterDataDir"]) assert.Equal(t, filepath.Join(dataDir, "k0s"), values["embeddedClusterK0sDir"]) - assert.Equal(t, true, values["isEmbeddedClusterV3"]) + // TODO: enable this once we stop relying on KOTS to deploy the app + // assert.Equal(t, true, values["isEmbeddedClusterV3"]) assert.Contains(t, values["extraEnv"], map[string]interface{}{ "name": "SSL_CERT_CONFIGMAP", diff --git a/pkg/addons/openebs/static/metadata.yaml b/pkg/addons/openebs/static/metadata.yaml index bbe86739e6..b179753a57 100644 --- a/pkg/addons/openebs/static/metadata.yaml +++ b/pkg/addons/openebs/static/metadata.yaml @@ -16,8 +16,8 @@ images: openebs-linux-utils: repo: proxy.replicated.com/library/openebs-linux-utils tag: - amd64: 4.2.0-amd64@sha256:0e3c39e01c170593a4baab34ad3469e08e106f75e64203b2e87f9c2c55f9dae7 - arm64: 4.2.0-arm64@sha256:da72692224cb9fd7e0120297bd53f4bdeb45ec6280822846afd292aae1b89fe1 + amd64: 4.3.0-amd64@sha256:df790b30c5f472d71ca6102164bd7d2af0a21ccea4f61db42d6d70a29fb0e3cb + arm64: 4.3.0-arm64@sha256:948a417a826731ffeb83cde18f2610710425d674436531f04e658a8eba690d25 openebs-provisioner-localpv: repo: proxy.replicated.com/library/openebs-provisioner-localpv tag: diff --git a/pkg/addons/velero/static/metadata.yaml b/pkg/addons/velero/static/metadata.yaml index 0d6edd5d0b..113d252fea 100644 --- a/pkg/addons/velero/static/metadata.yaml +++ b/pkg/addons/velero/static/metadata.yaml @@ -11,8 +11,8 @@ images: kubectl: repo: proxy.replicated.com/library/kubectl tag: - amd64: 1.34.2-amd64@sha256:5383cd50f71e8971700afc69f67c91113a09f05905b04df1a61257854190f26c - arm64: 1.34.2-arm64@sha256:c976b98fa758b16152b8da0dab7e3d7e668aded6f0a859ec7afb36e8ee474827 + amd64: 1.34.2-amd64@sha256:3ee82c4fbdcd324861aa437e65763f935bcd95b905f2dcf8bc7254d6f0f861d5 + arm64: 1.34.2-arm64@sha256:0524a9c1e29157bcbb62e5f065791371f495357be4e29a9e4c8f8dda400d90c6 velero: repo: proxy.replicated.com/library/velero tag: diff --git a/tests/dryrun/v3_install_test.go b/tests/dryrun/v3_install_test.go index 5ac13dda02..7c26189c93 100644 --- a/tests/dryrun/v3_install_test.go +++ b/tests/dryrun/v3_install_test.go @@ -179,11 +179,12 @@ func validateHappyPathOnline(t *testing.T, hcli *helm.MockClient) { adminConsoleOpts, found := isHelmReleaseInstalled(hcli, "admin-console") require.True(t, found, "admin-console helm release should be installed") assertHelmValues(t, adminConsoleOpts.Values, map[string]any{ - "isAirgap": false, - "isMultiNodeEnabled": true, - "embeddedClusterID": in.Spec.ClusterID, - "isEmbeddedClusterV3": true, - "kurlProxy.enabled": false, + "isAirgap": false, + "isMultiNodeEnabled": true, + "embeddedClusterID": in.Spec.ClusterID, + // TODO: enable this once we stop relying on KOTS to deploy the app + // "isEmbeddedClusterV3": true, + "kurlProxy.enabled": false, }) // Validate that registry addon is NOT installed for online installations diff --git a/versions.mk b/versions.mk index a08a548706..c8257bba6c 100644 --- a/versions.mk +++ b/versions.mk @@ -20,14 +20,14 @@ K0S_GO_VERSION = $(K0S_VERSION_1_$(K0S_MINOR_VERSION)) TROUBLESHOOT_VERSION = v0.122.0 # Helm Version -HELM_VERSION = v3.19.0 +HELM_VERSION = v3.19.2 # FIO Version (for performance testing) FIO_VERSION = 3.41 # Kubernetes Development Tool Versions CONTROLLER_TOOLS_VERSION = v0.19.0 -KUSTOMIZE_VERSION = v5.7.1 +KUSTOMIZE_VERSION = v5.8.0 ### Overrides ### From dc35d4118b6b20a089d4efcb94b4b2f1bfb38eb2 Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Mon, 8 Dec 2025 11:42:37 -0800 Subject: [PATCH 04/11] f --- .github/workflows/ci.yaml | 33 +-- .../local-artifact-mirror/Dockerfile.ttlsh | 2 +- dev/dockerfiles/operator/Dockerfile.local | 2 +- dev/dockerfiles/operator/Dockerfile.ttlsh | 2 +- go.mod | 116 ++++---- go.sum | 267 +++++++++--------- kinds/go.mod | 28 +- kinds/go.sum | 75 +++-- tests/dryrun/Dockerfile | 2 +- utils/go.mod | 2 +- web/package-lock.json | 48 ++-- web/package.json | 6 +- 12 files changed, 279 insertions(+), 304 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 266ab75b00..1d17740911 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -267,31 +267,8 @@ jobs: with: go-version-file: go.mod cache-dependency-path: "**/*.sum" - - name: Free up runner disk space # this is much faster than .github/actions/free-disk-space - run: | - df -h - sudo rm -rf \ - /usr/share/swift \ - /usr/share/dotnet \ - /usr/lib/jvm \ - /usr/local/share/boost \ - /usr/local/lib/heroku \ - /usr/local/julia* \ - /usr/local/.ghcup \ - /usr/local/share/powershell \ - /usr/local/bin/aliyun \ - /usr/local/bin/azcopy \ - /usr/local/bin/bicep \ - /usr/local/bin/cpack \ - /usr/local/bin/hub \ - /usr/local/bin/minikube \ - /usr/local/bin/packer \ - /usr/local/bin/pulumi* \ - /usr/local/bin/sam \ - /usr/local/bin/stack \ - /usr/local/bin/terraform \ - /usr/local/bin/oc - df -h + - name: Free up runner disk space + run: *free-disk-space - name: Install kind uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab with: @@ -307,6 +284,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 + - name: Free up runner disk space + run: *free-disk-space - name: Go cache uses: actions/cache@v4 with: @@ -456,7 +435,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Free up runner disk space # this is much faster than .github/actions/free-disk-space + - name: Free up runner disk space run: *free-disk-space - name: Cache embedded bins @@ -612,7 +591,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Free up runner disk space # this is much faster than .github/actions/free-disk-space + - name: Free up runner disk space run: *free-disk-space - name: Cache embedded bins diff --git a/dev/dockerfiles/local-artifact-mirror/Dockerfile.ttlsh b/dev/dockerfiles/local-artifact-mirror/Dockerfile.ttlsh index 1375ed5dcb..81736c413f 100644 --- a/dev/dockerfiles/local-artifact-mirror/Dockerfile.ttlsh +++ b/dev/dockerfiles/local-artifact-mirror/Dockerfile.ttlsh @@ -1,4 +1,4 @@ -FROM golang:1.25.3 AS build +FROM golang:1.25.4 AS build WORKDIR /app diff --git a/dev/dockerfiles/operator/Dockerfile.local b/dev/dockerfiles/operator/Dockerfile.local index 3ee9d985ed..cd5d0b9f4f 100644 --- a/dev/dockerfiles/operator/Dockerfile.local +++ b/dev/dockerfiles/operator/Dockerfile.local @@ -1,4 +1,4 @@ -FROM golang:1.25.3-alpine AS build +FROM golang:1.25.4-alpine AS build RUN apk add --no-cache ca-certificates curl git make bash helm diff --git a/dev/dockerfiles/operator/Dockerfile.ttlsh b/dev/dockerfiles/operator/Dockerfile.ttlsh index 1a4ff8457e..278a4b4fc2 100644 --- a/dev/dockerfiles/operator/Dockerfile.ttlsh +++ b/dev/dockerfiles/operator/Dockerfile.ttlsh @@ -1,4 +1,4 @@ -FROM golang:1.25.3 AS build +FROM golang:1.25.4 AS build WORKDIR /app diff --git a/go.mod b/go.mod index 9525c39ae9..03701fecfc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/replicatedhq/embedded-cluster -go 1.25.3 +go 1.25.4 require ( github.com/AlecAivazis/survey/v2 v2.3.7 @@ -28,44 +28,45 @@ require ( github.com/gorilla/mux v1.8.1 github.com/gosimple/slug v1.15.0 github.com/hashicorp/go-retryablehttp v0.7.8 - github.com/jedib0t/go-pretty/v6 v6.6.8 + github.com/jedib0t/go-pretty/v6 v6.7.5 github.com/k0sproject/k0s v1.33.7-0.20251119112034-a756314d3a78 github.com/mattn/go-isatty v0.0.20 - github.com/ohler55/ojg v1.26.10 + github.com/ohler55/ojg v1.27.0 github.com/onsi/ginkgo/v2 v2.27.2 github.com/onsi/gomega v1.38.2 github.com/pkg/errors v0.9.1 github.com/replicatedhq/embedded-cluster/kinds v0.0.0 github.com/replicatedhq/embedded-cluster/utils v0.0.0 github.com/replicatedhq/kotskinds v0.0.0-20251024162531-2174a5b85a4d - github.com/replicatedhq/troubleshoot v0.123.12 + github.com/replicatedhq/troubleshoot v0.123.15 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 github.com/swaggo/http-swagger/v2 v2.0.2 github.com/swaggo/swag/v2 v2.0.0-rc4 - github.com/tiendc/go-deepcopy v1.7.1 + github.com/tiendc/go-deepcopy v1.7.2 github.com/urfave/cli/v2 v2.27.7 - github.com/vmware-tanzu/velero v1.17.0 + github.com/vmware-tanzu/velero v1.17.1 go.uber.org/multierr v1.11.0 go.yaml.in/yaml/v3 v3.0.4 golang.org/x/crypto v0.45.0 golang.org/x/term v0.37.0 gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible - helm.sh/helm/v3 v3.19.0 - k8s.io/api v0.34.1 - k8s.io/apiextensions-apiserver v0.34.1 - k8s.io/apimachinery v0.34.1 - k8s.io/cli-runtime v0.34.1 - k8s.io/client-go v0.34.1 - k8s.io/component-helpers v0.34.1 - k8s.io/kubectl v0.34.1 + helm.sh/helm/v3 v3.19.2 + k8s.io/api v0.34.2 + k8s.io/apiextensions-apiserver v0.34.2 + k8s.io/apimachinery v0.34.2 + k8s.io/cli-runtime v0.34.2 + k8s.io/client-go v0.34.2 + k8s.io/component-helpers v0.34.2 + k8s.io/kubectl v0.34.2 k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 oras.land/oras-go/v2 v2.6.0 - sigs.k8s.io/controller-runtime v0.22.3 + sigs.k8s.io/controller-runtime v0.22.4 sigs.k8s.io/yaml v1.6.0 ) @@ -77,9 +78,9 @@ replace ( require ( cel.dev/expr v0.24.0 // indirect cloud.google.com/go v0.121.6 // indirect - cloud.google.com/go/auth v0.16.5 // indirect + cloud.google.com/go/auth v0.17.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.8.0 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.2 // indirect cloud.google.com/go/monitoring v1.24.2 // indirect cloud.google.com/go/storage v1.56.2 // indirect @@ -88,7 +89,7 @@ require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.5.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect @@ -127,10 +128,10 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/chzyer/readline v1.5.1 // indirect - github.com/cilium/ebpf v0.19.0 // indirect + github.com/cilium/ebpf v0.20.0 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect - github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect - github.com/containerd/cgroups/v3 v3.0.5 // indirect + github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f // indirect + github.com/containerd/cgroups/v3 v3.1.1 // indirect github.com/containerd/containerd v1.7.29 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect @@ -145,19 +146,19 @@ require ( github.com/coreos/go-semver v0.3.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect - github.com/cyphar/filepath-securejoin v0.5.1 // indirect + github.com/cyphar/filepath-securejoin v0.6.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/distribution/v3 v3.0.0 // indirect - github.com/docker/cli v28.3.2+incompatible // indirect + github.com/docker/cli v28.5.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v28.3.3+incompatible // indirect + github.com/docker/docker v28.5.1+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.3 // indirect - github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.9.0 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.35.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect @@ -190,13 +191,13 @@ require ( github.com/google/cel-go v0.26.1 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/go-containerregistry v0.20.3 // indirect + github.com/google/go-containerregistry v0.20.6 // indirect github.com/google/go-intervals v0.0.2 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -209,7 +210,7 @@ require ( github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter v1.8.2 // indirect + github.com/hashicorp/go-getter v1.8.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect @@ -230,7 +231,6 @@ require ( github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect github.com/lib/pq v1.10.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e // indirect @@ -263,10 +263,11 @@ require ( github.com/muhlemmer/gu v0.3.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/nxadm/tail v1.4.11 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/opencontainers/runtime-spec v1.2.1 // indirect + github.com/opencontainers/runtime-spec v1.3.0 // indirect github.com/opencontainers/selinux v1.13.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect @@ -278,24 +279,26 @@ require ( github.com/proglottis/gpgme v0.1.4 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.1 // indirect + github.com/prometheus/common v0.67.4 // indirect github.com/prometheus/procfs v0.17.0 // indirect + github.com/redis/go-redis/extra/redisotel/v9 v9.5.3 // indirect + github.com/redis/go-redis/v9 v9.10.0 // indirect github.com/rubenv/sql-migrate v1.8.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect - github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect github.com/segmentio/ksuid v1.0.4 // indirect github.com/sergi/go-diff v1.4.0 // indirect - github.com/shirou/gopsutil/v4 v4.25.9 // indirect + github.com/shirou/gopsutil/v4 v4.25.10 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/sigstore/fulcio v1.6.6 // indirect - github.com/sigstore/protobuf-specs v0.4.1 // indirect - github.com/sigstore/sigstore v1.9.5 // indirect + github.com/sigstore/fulcio v1.8.3 // indirect + github.com/sigstore/protobuf-specs v0.5.0 // indirect + github.com/sigstore/sigstore v1.10.0 // indirect github.com/smallstep/pkcs7 v0.1.1 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect github.com/stoewer/go-strcase v1.3.1 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -305,7 +308,6 @@ require ( github.com/swaggo/swag v1.8.1 // indirect github.com/sylabs/sif/v2 v2.21.1 // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect - github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/numcpus v0.10.0 // indirect github.com/ulikunitz/xz v0.5.15 // indirect @@ -317,7 +319,6 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - github.com/zeebo/errs v1.4.0 // indirect github.com/zitadel/logging v0.6.2 // indirect github.com/zitadel/oidc/v3 v3.45.0 // indirect github.com/zitadel/schema v1.3.1 // indirect @@ -326,9 +327,9 @@ require ( go.etcd.io/etcd/client/v3 v3.6.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.38.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect @@ -338,39 +339,38 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.8.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/exp v0.0.0-20251009144603-d2f985daa21b // indirect golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.32.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.31.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.38.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/api v0.249.0 // indirect + google.golang.org/api v0.256.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251014184007-4626949a642f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f // indirect - google.golang.org/grpc v1.76.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect + google.golang.org/grpc v1.77.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiserver v0.34.1 // indirect + k8s.io/apiserver v0.34.2 // indirect k8s.io/cloud-provider v0.33.6 // indirect - k8s.io/component-base v0.34.1 // indirect + k8s.io/component-base v0.34.2 // indirect k8s.io/controller-manager v0.33.6 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kms v0.34.1 // indirect + k8s.io/kms v0.34.2 // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect k8s.io/kubelet v0.34.1 // indirect k8s.io/kubernetes v1.34.1 // indirect - k8s.io/metrics v0.34.1 // indirect - oras.land/oras-go v1.2.6 // indirect + k8s.io/metrics v0.34.2 // indirect + oras.land/oras-go v1.2.7 // indirect periph.io/x/host/v3 v3.8.5 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect @@ -379,3 +379,5 @@ require ( sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) + +replace github.com/cyphar/filepath-securejoin => github.com/cyphar/filepath-securejoin v0.5.2 diff --git a/go.sum b/go.sum index 77def34cdf..22775dc1ec 100644 --- a/go.sum +++ b/go.sum @@ -3,12 +3,12 @@ cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= -cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= -cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= +cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= -cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= @@ -29,27 +29,27 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 h1:E4MgwLBGeVB5f2MdcIVD3ELVAWpr+WD6MUe1i+tM/PA= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0/go.mod h1:Y2b/1clN4zsAoUd/pgNAQHjLDnTis/6ROkUfyob6psM= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= -github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= @@ -173,16 +173,16 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/cilium/ebpf v0.19.0 h1:Ro/rE64RmFBeA9FGjcTc+KmCeY6jXmryu6FfnzPRIao= -github.com/cilium/ebpf v0.19.0/go.mod h1:fLCgMo3l8tZmAdM3B2XqdFzXBpwkcSTroaVqN08OWVY= +github.com/cilium/ebpf v0.20.0 h1:atwWj9d3NffHyPZzVlx3hmw1on5CLe9eljR8VuHTwhM= +github.com/cilium/ebpf v0.20.0/go.mod h1:pzLjFymM+uZPLk/IXZUL63xdx5VXEo+enTzxkZXdycw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= -github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo= -github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +github.com/containerd/cgroups/v3 v3.1.1 h1:ASZmQGfOHbRj43/1aMn5QcWIsv0R/AuHHDNCguRY0p0= +github.com/containerd/cgroups/v3 v3.1.1/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE= github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= @@ -219,8 +219,8 @@ github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= -github.com/cyphar/filepath-securejoin v0.5.1 h1:eYgfMq5yryL4fbWfkLpFFy2ukSELzaJOTaUTuh+oF48= -github.com/cyphar/filepath-securejoin v0.5.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.5.2 h1:w/T2bhKr4pgwG0SUGjU4S/Is9+zUknLh5ROTJLzWX8E= +github.com/cyphar/filepath-securejoin v0.5.2/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -233,16 +233,16 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY= -github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY= +github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= -github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM= +github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= @@ -260,10 +260,10 @@ github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= -github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= -github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= -github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= +github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= +github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -287,6 +287,7 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= @@ -391,8 +392,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= -github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= +github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= +github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4= github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4= github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM= @@ -412,8 +413,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= -github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= @@ -433,10 +434,10 @@ github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99 h1:JYghRBlGCZyCF2wNUJ8W0cwaQdtpcssJ4CgC406g+WU= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= @@ -448,8 +449,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.8.2 h1:CGCK+bZQLl44PYiwJweVzfpjg7bBwtuXu3AGcLiod2o= -github.com/hashicorp/go-getter v1.8.2/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4= +github.com/hashicorp/go-getter v1.8.3 h1:gIS+oTNv3kyYAvlUVgMR46MiG0bM0KuSON/KZEvRoRg= +github.com/hashicorp/go-getter v1.8.3/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -458,7 +459,6 @@ github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVU github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru/arc/v2 v2.0.7 h1:QxkVTxwColcduO+LP7eJO56r2hFiG8zEbfAAzRv52KQ= github.com/hashicorp/golang-lru/arc/v2 v2.0.7/go.mod h1:Pe7gBlGdc8clY5LJ0LpJXMt5AmgmWNH1g+oFFVUHOEc= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -478,12 +478,10 @@ github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc= -github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= +github.com/jedib0t/go-pretty/v6 v6.7.5 h1:9dJSWTJnsXJVVAbvxIFxeHf/JxoJd7GUl5o3UzhtuiM= +github.com/jedib0t/go-pretty/v6 v6.7.5/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jeremija/gosubmit v0.2.8 h1:mmSITBz9JxVtu8eqbN+zmmwX7Ij2RidQxhcwRVI4wqA= github.com/jeremija/gosubmit v0.2.8/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= -github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= -github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= @@ -532,8 +530,6 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ= -github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= @@ -632,8 +628,8 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= -github.com/ohler55/ojg v1.26.10 h1:qXq8A0AjzwvO+rKJWv9apNVWxyu3He8lgGZZ+AoEdLA= -github.com/ohler55/ojg v1.26.10/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= +github.com/ohler55/ojg v1.27.0 h1:1JzdkMpDc/X9bzRaN1+8AFLnrSiFy96yDSaeACCGD5U= +github.com/ohler55/ojg v1.27.0/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -650,8 +646,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww= -github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= +github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE= github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= @@ -699,24 +695,24 @@ github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvM github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.67.1 h1:OTSON1P4DNxzTg4hmKCc37o4ZAZDv0cfXLkOt0oEowI= -github.com/prometheus/common v0.67.1/go.mod h1:RpmT9v35q2Y+lsieQsdOh5sXZ6ajUGC8NjZAmr8vb0Q= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= -github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho= -github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= -github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= -github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3 h1:1/BDligzCa40GTllkDnY3Y5DTHuKCONbB2JcRyIfl20= +github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3/go.mod h1:3dZmcLn3Qw6FLlWASn1g4y+YO9ycEFUOM+bhBmzLVKQ= +github.com/redis/go-redis/extra/redisotel/v9 v9.5.3 h1:kuvuJL/+MZIEdvtb/kTBRiRgYaOmx1l+lYJyVdrRUOs= +github.com/redis/go-redis/extra/redisotel/v9 v9.5.3/go.mod h1:7f/FMrf5RRRVHXgfk7CzSVzXHiWeuOQUu2bsVqWoa+g= +github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs= +github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/replicatedhq/kotskinds v0.0.0-20251024162531-2174a5b85a4d h1:N8t9W5SYs1MKPsuAp4PA5Haje4cOyCyubAq65qB1wzE= github.com/replicatedhq/kotskinds v0.0.0-20251024162531-2174a5b85a4d/go.mod h1:+k4PHo2wukoU9kdiKrqqgi89Wmj+9AiwppYGVK11zig= -github.com/replicatedhq/troubleshoot v0.123.12 h1:XbgZJMSwIHyf1lvxIRNwI9AVsRzcA7N3AWLPLSkrr+w= -github.com/replicatedhq/troubleshoot v0.123.12/go.mod h1:CKPCj8si77XuSL6sIAFdqtO23/eha159eEBlQF8HpVw= +github.com/replicatedhq/troubleshoot v0.123.15 h1:znJpO0oxln+5247mScmsovwhycDa+3Wm28ZCuWQ01eg= +github.com/replicatedhq/troubleshoot v0.123.15/go.mod h1:f9/bTb5p6LCgpQqqvbOqUwqTCkqGT8kc8wzZuFkX0xM= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= @@ -731,22 +727,22 @@ github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEV github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY= github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= -github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= -github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= +github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g= +github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shirou/gopsutil/v4 v4.25.9 h1:JImNpf6gCVhKgZhtaAHJ0serfFGtlfIlSC08eaKdTrU= -github.com/shirou/gopsutil/v4 v4.25.9/go.mod h1:gxIxoC+7nQRwUl/xNhutXlD8lq+jxTgpIkEf3rADHL8= +github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA= +github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sigstore/fulcio v1.6.6 h1:XaMYX6TNT+8n7Npe8D94nyZ7/ERjEsNGFC+REdi/wzw= -github.com/sigstore/fulcio v1.6.6/go.mod h1:BhQ22lwaebDgIxVBEYOOqLRcN5+xOV+C9bh/GUXRhOk= -github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc= -github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= -github.com/sigstore/sigstore v1.9.5 h1:Wm1LT9yF4LhQdEMy5A2JeGRHTrAWGjT3ubE5JUSrGVU= -github.com/sigstore/sigstore v1.9.5/go.mod h1:VtxgvGqCmEZN9X2zhFSOkfXxvKUjpy8RpUW39oCtoII= +github.com/sigstore/fulcio v1.8.3 h1:zkuAkRHbD53hhYGlBHHeAW4NRDrrTiDHumAbcfSyyFw= +github.com/sigstore/fulcio v1.8.3/go.mod h1:YxP7TTdn9H5Gg+dXOsu61X36LLYxT2ZuvODhWelMNwA= +github.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY= +github.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= +github.com/sigstore/sigstore v1.10.0 h1:lQrmdzqlR8p9SCfWIpFoGUqdXEzJSZT2X+lTXOMPaQI= +github.com/sigstore/sigstore v1.10.0/go.mod h1:Ygq+L/y9Bm3YnjpJTlQrOk/gXyrjkpn3/AEJpmk1n9Y= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -759,15 +755,15 @@ github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= -github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw= github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= @@ -813,10 +809,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4= -github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= -github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= -github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= +github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44= +github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= @@ -835,8 +829,8 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -github.com/vmware-tanzu/velero v1.17.0 h1:b+KLlBG+v1YKogP81nAFix2pgJBTmUrnVlXg+OfB5ao= -github.com/vmware-tanzu/velero v1.17.0/go.mod h1:BJRFKei89hSqrazQKiwv5YhhX871X1W1qPyo5OP09zw= +github.com/vmware-tanzu/velero v1.17.1 h1:ldKeiTuUwkThOw7zrUucNA1NwnLG66zl13YetWAoE0I= +github.com/vmware-tanzu/velero v1.17.1/go.mod h1:3KTxuUN6Un38JzmYAX+8U6j2k6EexGoNNxa8jrJML8U= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= @@ -850,8 +844,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= -github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zitadel/logging v0.6.2 h1:MW2kDDR0ieQynPZ0KIZPrh9ote2WkxfBif5QoARDQcU= github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4= github.com/zitadel/oidc/v3 v3.45.0 h1:SaVJ2kdcJi/zdEWWlAns+81VxmfdYX4E+2mWFVIH7Ec= @@ -878,14 +870,14 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk= -go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw= -go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= +go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs= +go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4= go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= @@ -928,8 +920,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -986,8 +978,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= -golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1029,6 +1021,7 @@ golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1065,8 +1058,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1091,8 +1084,8 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w= -google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ= +google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= +google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1100,18 +1093,18 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20251014184007-4626949a642f h1:OiFuztEyBivVKDvguQJYWq1yDcfAHIID/FVrPR4oiI0= -google.golang.org/genproto/googleapis/api v0.0.0-20251014184007-4626949a642f/go.mod h1:kprOiu9Tr0JYyD6DORrc4Hfyk3RFXqkQ3ctHEum3ZbM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f h1:1FTH6cpXFsENbPR5Bu8NQddPSaUUE6NA2XdZdDSAJK4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= -google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1152,57 +1145,57 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -helm.sh/helm/v3 v3.19.0 h1:krVyCGa8fa/wzTZgqw0DUiXuRT5BPdeqE/sQXujQ22k= -helm.sh/helm/v3 v3.19.0/go.mod h1:Lk/SfzN0w3a3C3o+TdAKrLwJ0wcZ//t1/SDXAvfgDdc= +helm.sh/helm/v3 v3.19.2 h1:psQjaM8aIWrSVEly6PgYtLu/y6MRSmok4ERiGhZmtUY= +helm.sh/helm/v3 v3.19.2/go.mod h1:gX10tB5ErM+8fr7bglUUS/UfTOO8UUTYWIBH1IYNnpE= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= -k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= -k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= -k8s.io/cli-runtime v0.34.1 h1:btlgAgTrYd4sk8vJTRG6zVtqBKt9ZMDeQZo2PIzbL7M= -k8s.io/cli-runtime v0.34.1/go.mod h1:aVA65c+f0MZiMUPbseU/M9l1Wo2byeaGwUuQEQVVveE= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= +k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo= +k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.2 h1:2/yu8suwkmES7IzwlehAovo8dDE07cFRC7KMDb1+MAE= +k8s.io/apiserver v0.34.2/go.mod h1:gqJQy2yDOB50R3JUReHSFr+cwJnL8G1dzTA0YLEqAPI= +k8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE= +k8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/cloud-provider v0.33.6 h1:XLt8UhgsZgou8Ua6KQrnnHGS3lpZRHlbNhnO4kPyO0c= k8s.io/cloud-provider v0.33.6/go.mod h1:I08jJ7obAWjv9z5ZcabdOV3aizfo7ru495xuxlHQw84= -k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A= -k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0= -k8s.io/component-helpers v0.34.1 h1:gWhH3CCdwAx5P3oJqZKb4Lg5FYZTWVbdWtOI8n9U4XY= -k8s.io/component-helpers v0.34.1/go.mod h1:4VgnUH7UA/shuBur+OWoQC0xfb69sy/93ss0ybZqm3c= +k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= +k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= +k8s.io/component-helpers v0.34.2 h1:RIUGDdU+QFzeVKLZ9f05sXTNAtJrRJ3bnbMLrogCrvM= +k8s.io/component-helpers v0.34.2/go.mod h1:pLi+GByuRTeFjjcezln8gHL7LcT6HImkwVQ3A2SQaEE= k8s.io/controller-manager v0.33.6 h1:md2KM5RabX7KTTly1KmNDbNr5YJvLtse7sJOX2we8tI= k8s.io/controller-manager v0.33.6/go.mod h1:dtCyZIG+CNFI8hY/lQY7ZAOO20VxGm+lpi1do2fmLGY= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kms v0.34.1 h1:iCFOvewDPzWM9fMTfyIPO+4MeuZ0tcZbugxLNSHFG4w= -k8s.io/kms v0.34.1/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= +k8s.io/kms v0.34.2 h1:91rj4MDZLyIT9KxG8J5/CcMH666Z88CF/xJQeuPfJc8= +k8s.io/kms v0.34.2/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= -k8s.io/kubectl v0.34.1 h1:1qP1oqT5Xc93K+H8J7ecpBjaz511gan89KO9Vbsh/OI= -k8s.io/kubectl v0.34.1/go.mod h1:JRYlhJpGPyk3dEmJ+BuBiOB9/dAvnrALJEiY/C5qa6A= +k8s.io/kubectl v0.34.2 h1:+fWGrVlDONMUmmQLDaGkQ9i91oszjjRAa94cr37hzqA= +k8s.io/kubectl v0.34.2/go.mod h1:X2KTOdtZZNrTWmUD4oHApJ836pevSl+zvC5sI6oO2YQ= k8s.io/kubelet v0.34.1 h1:doAaTA9/Yfzbdq/u/LveZeONp96CwX9giW6b+oHn4m4= k8s.io/kubelet v0.34.1/go.mod h1:PtV3Ese8iOM19gSooFoQT9iyRisbmJdAPuDImuccbbA= k8s.io/kubernetes v1.34.1 h1:F3p8dtpv+i8zQoebZeK5zBqM1g9x1aIdnA5vthvcuUk= k8s.io/kubernetes v1.34.1/go.mod h1:iu+FhII+Oc/1gGWLJcer6wpyih441aNFHl7Pvm8yPto= -k8s.io/metrics v0.34.1 h1:374Rexmp1xxgRt64Bi0TsjAM8cA/Y8skwCoPdjtIslE= -k8s.io/metrics v0.34.1/go.mod h1:Drf5kPfk2NJrlpcNdSiAAHn/7Y9KqxpRNagByM7Ei80= +k8s.io/metrics v0.34.2 h1:zao91FNDVPRGIiHLO2vqqe21zZVPien1goyzn0hsz90= +k8s.io/metrics v0.34.2/go.mod h1:Ydulln+8uZZctUM8yrUQX4rfq/Ay6UzsuXf24QJ37Vc= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go v1.2.6 h1:z8cmxQXBU8yZ4mkytWqXfo6tZcamPwjsuxYU81xJ8Lk= -oras.land/oras-go v1.2.6/go.mod h1:OVPc1PegSEe/K8YiLfosrlqlqTN9PUyFvOw5Y9gwrT8= +oras.land/oras-go v1.2.7 h1:KF9rBAtKYMGB5gjgHV5XquUfYDER3ecQBEXjdI7KZWI= +oras.land/oras-go v1.2.7/go.mod h1:WVpIPbm82xjWT/GJU3TqZ0y9Ctj3DGco4wLYvGdOVvA= oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o= periph.io/x/host/v3 v3.8.5 h1:g4g5xE1XZtDiGl1UAJaUur1aT7uNiFLMkyMEiZ7IHII= periph.io/x/host/v3 v3.8.5/go.mod h1:hPq8dISZIc+UNfWoRj+bPH3XEBQqJPdFdx218W92mdc= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 h1:XotDXzqvJ8Nx5eiZZueLpTuafJz8SiodgOemI+w87QU= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y= -sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= diff --git a/kinds/go.mod b/kinds/go.mod index f910224fb7..0474255554 100644 --- a/kinds/go.mod +++ b/kinds/go.mod @@ -1,52 +1,54 @@ module github.com/replicatedhq/embedded-cluster/kinds -go 1.25.3 +go 1.25.4 require ( github.com/google/uuid v1.6.0 github.com/k0sproject/dig v0.4.0 - github.com/k0sproject/k0s v1.33.5-0.20250819091818-6da1d9c31be6 + github.com/k0sproject/k0s v1.33.7-0.20251119112034-a756314d3a78 github.com/stretchr/testify v1.11.1 go.yaml.in/yaml/v3 v3.0.4 - k8s.io/api v0.34.1 - k8s.io/apimachinery v0.34.1 - sigs.k8s.io/controller-runtime v0.22.3 + k8s.io/api v0.34.2 + k8s.io/apimachinery v0.34.2 + sigs.k8s.io/controller-runtime v0.22.4 sigs.k8s.io/yaml v1.6.0 ) require ( + github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/cyphar/filepath-securejoin v0.5.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/onsi/ginkgo/v2 v2.23.4 // indirect - github.com/onsi/gomega v1.36.3 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/net v0.41.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect helm.sh/helm/v3 v3.18.6 // indirect k8s.io/apiextensions-apiserver v0.34.1 // indirect - k8s.io/client-go v0.34.1 // indirect + k8s.io/client-go v0.34.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect diff --git a/kinds/go.sum b/kinds/go.sum index 1e0229136c..b11bec8cf2 100644 --- a/kinds/go.sum +++ b/kinds/go.sum @@ -1,11 +1,12 @@ -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.5.2 h1:w/T2bhKr4pgwG0SUGjU4S/Is9+zUknLh5ROTJLzWX8E= +github.com/cyphar/filepath-securejoin v0.5.2/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -25,16 +26,16 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= -github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/k0sproject/dig v0.4.0 h1:yBxFUUxNXAMGBg6b7c6ypxdx/o3RmhoI5v5ABOw5tn0= github.com/k0sproject/dig v0.4.0/go.mod h1:rlZ7N7ZEcB4Fi96TPXkZ4dqyAiDWOGLapyL9YpZ7Qz4= -github.com/k0sproject/k0s v1.33.5-0.20250819091818-6da1d9c31be6 h1:g5Jg/vHENLY1UnxDLkzcXLxxmSwLIxXIJNWlRAFPP6s= -github.com/k0sproject/k0s v1.33.5-0.20250819091818-6da1d9c31be6/go.mod h1:DUWIG4PCnk8w0muU+mlE5VZq2vvKrB//XBcYvHKVK54= +github.com/k0sproject/k0s v1.33.7-0.20251119112034-a756314d3a78 h1:lrA3V3mlmLKkHN01/f0lx4MF+x5ijYSsH2DdOhhwA1Y= +github.com/k0sproject/k0s v1.33.7-0.20251119112034-a756314d3a78/go.mod h1:MOvHATSyFUudgQCXg7fD6r86hqFljRqz43QvbIgrKYs= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -51,10 +52,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= -github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= -github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= -github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -68,8 +69,8 @@ github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEV github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -83,10 +84,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -94,37 +93,37 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -139,20 +138,20 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= helm.sh/helm/v3 v3.18.6 h1:S/2CqcYnNfLckkHLI0VgQbxgcDaU3N4A/46E3n9wSNY= helm.sh/helm/v3 v3.18.6/go.mod h1:L/dXDR2r539oPlFP1PJqKAC1CUgqHJDLkxKpDGrWnyg= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y= -sigs.k8s.io/controller-runtime v0.22.3/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= diff --git a/tests/dryrun/Dockerfile b/tests/dryrun/Dockerfile index 49777d431d..50a3e3c978 100644 --- a/tests/dryrun/Dockerfile +++ b/tests/dryrun/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.25.3-alpine AS build +FROM golang:1.25.4-alpine AS build RUN apk add --no-cache ca-certificates curl git make bash diff --git a/utils/go.mod b/utils/go.mod index 2e989fa876..ef8096047d 100644 --- a/utils/go.mod +++ b/utils/go.mod @@ -1,6 +1,6 @@ module github.com/replicatedhq/embedded-cluster/utils -go 1.25.3 +go 1.25.4 require github.com/stretchr/testify v1.11.1 diff --git a/web/package-lock.json b/web/package-lock.json index 8d66938a39..f2c79e6ae3 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -12,12 +12,12 @@ "@tailwindcss/postcss": "^4.1.17", "@tailwindcss/typography": "^0.5.19", "@tanstack/react-query": "^5.90.11", - "lucide-react": "^0.555.0", + "lucide-react": "^0.556.0", "openapi-fetch": "^0.15.0", "react": "^19.1.2", - "react-dom": "^19.1.2", + "react-dom": "^19.2.1", "react-markdown": "^10.1.0", - "react-router-dom": "^7.9.6", + "react-router-dom": "^7.10.1", "remark-gfm": "^4.0.1" }, "devDependencies": { @@ -11356,9 +11356,9 @@ } }, "node_modules/lucide-react": { - "version": "0.555.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.555.0.tgz", - "integrity": "sha512-D8FvHUGbxWBRQM90NZeIyhAvkFfsh3u9ekrMvJ30Z6gnpBHS6HC6ldLg7tL45hwiIz/u66eKDtdA23gwwGsAHA==", + "version": "0.556.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.556.0.tgz", + "integrity": "sha512-iOb8dRk7kLaYBZhR2VlV1CeJGxChBgUthpSP8wom9jfj79qovgG6qcSdiy6vkoREKPnbUYzJsCn4o4PtG3Iy+A==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -13748,24 +13748,24 @@ "license": "MIT" }, "node_modules/react": { - "version": "19.1.2", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.2.tgz", - "integrity": "sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w==", + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", + "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.2.tgz", - "integrity": "sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g==", + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz", + "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==", "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.2" + "react": "^19.2.1" } }, "node_modules/react-is": { @@ -13814,9 +13814,9 @@ } }, "node_modules/react-router": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz", - "integrity": "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.1.tgz", + "integrity": "sha512-gHL89dRa3kwlUYtRQ+m8NmxGI6CgqN+k4XyGjwcFoQwwCWF6xXpOCUlDovkXClS0d0XJN/5q7kc5W3kiFEd0Yw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -13836,12 +13836,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.6.tgz", - "integrity": "sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.1.tgz", + "integrity": "sha512-JNBANI6ChGVjA5bwsUIwJk7LHKmqB4JYnYfzFwyp2t12Izva11elds2jx7Yfoup2zssedntwU0oZ5DEmk5Sdaw==", "license": "MIT", "dependencies": { - "react-router": "7.9.6" + "react-router": "7.10.1" }, "engines": { "node": ">=20.0.0" @@ -14384,9 +14384,9 @@ } }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { diff --git a/web/package.json b/web/package.json index 133822dc60..9d5e1bb45f 100644 --- a/web/package.json +++ b/web/package.json @@ -23,12 +23,12 @@ "@tailwindcss/postcss": "^4.1.17", "@tailwindcss/typography": "^0.5.19", "@tanstack/react-query": "^5.90.11", - "lucide-react": "^0.555.0", + "lucide-react": "^0.556.0", "openapi-fetch": "^0.15.0", "react": "^19.1.2", - "react-dom": "^19.1.2", + "react-dom": "^19.2.1", "react-markdown": "^10.1.0", - "react-router-dom": "^7.9.6", + "react-router-dom": "^7.10.1", "remark-gfm": "^4.0.1" }, "devDependencies": { From 825c7f20ce7fef8ebe752e7c8064917fa51c1dbc Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Mon, 8 Dec 2025 11:44:28 -0800 Subject: [PATCH 05/11] f --- pkg/addons/openebs/static/metadata.yaml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pkg/addons/openebs/static/metadata.yaml b/pkg/addons/openebs/static/metadata.yaml index b179753a57..6064894f53 100644 --- a/pkg/addons/openebs/static/metadata.yaml +++ b/pkg/addons/openebs/static/metadata.yaml @@ -5,14 +5,9 @@ # $ make buildtools # $ output/bin/buildtools update addon # -version: 4.3.3 +version: 4.4.0 location: oci://proxy.replicated.com/anonymous/registry.replicated.com/ec-charts/openebs images: - kubectl: - repo: proxy.replicated.com/library/kubectl - tag: - amd64: 1.34.2-amd64@sha256:5383cd50f71e8971700afc69f67c91113a09f05905b04df1a61257854190f26c - arm64: 1.34.2-arm64@sha256:c976b98fa758b16152b8da0dab7e3d7e668aded6f0a859ec7afb36e8ee474827 openebs-linux-utils: repo: proxy.replicated.com/library/openebs-linux-utils tag: @@ -21,5 +16,5 @@ images: openebs-provisioner-localpv: repo: proxy.replicated.com/library/openebs-provisioner-localpv tag: - amd64: 4.3.0-amd64@sha256:7f3325a2249eba601fd222a23e27f741e9e6ddc5663ab9f5b8f7587930d1ef8d - arm64: 4.3.0-arm64@sha256:a218a9ad454a7452c663fa82453ec96b524faf220ccea8c7c2047ddc9c86fcfc + amd64: 4.4.0-amd64@sha256:a1e7eeeef83e48c86ce69e04855c301b93dab478aaf19e9cf8b4ae6433502c62 + arm64: 4.4.0-arm64@sha256:343f11c0b8708fa4ed3d60331fe3eaf1420871a51cac27cb7fedd0017ea49f7a From a494ae954ee054745db53199f3c5cc8fe4712a1e Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Mon, 8 Dec 2025 11:46:24 -0800 Subject: [PATCH 06/11] f --- web/package-lock.json | 2026 ++++++++++++++++++++++++++++++----------- web/package.json | 10 +- 2 files changed, 1509 insertions(+), 527 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index f2c79e6ae3..8eecb8a1a1 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -11,7 +11,7 @@ "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.17", "@tailwindcss/typography": "^0.5.19", - "@tanstack/react-query": "^5.90.11", + "@tanstack/react-query": "^5.90.12", "lucide-react": "^0.556.0", "openapi-fetch": "^0.15.0", "react": "^19.1.2", @@ -24,7 +24,7 @@ "@eslint/js": "^9.39.1", "@faker-js/faker": "^10.1.0", "@netlify/functions": "^5.0.1", - "@netlify/vite-plugin": "^2.7.14", + "@netlify/vite-plugin": "^2.7.15", "@tailwindcss/vite": "^4.1.17", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", @@ -41,14 +41,14 @@ "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", "jsdom": "^27.1.0", - "msw": "2.12.3", + "msw": "2.12.4", "openapi-backend": "^5.15.0", "openapi-typescript": "^7.10.1", "tailwindcss": "^4.1.13", "typescript": "^5.9.3", - "typescript-eslint": "^8.48.0", + "typescript-eslint": "^8.48.1", "unified": "^11.0.5", - "vite": "^7.2.4", + "vite": "^7.2.6", "vite-plugin-static-copy": "^3.1.4", "vitest": "^3.2.4" } @@ -2129,9 +2129,9 @@ "license": "MIT" }, "node_modules/@mapbox/node-pre-gyp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz", - "integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", + "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2241,28 +2241,28 @@ } }, "node_modules/@netlify/ai": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@netlify/ai/-/ai-0.3.3.tgz", - "integrity": "sha512-P2EWy+fmybtJ4xptqR1qq+fL0JK4kj7MxqziSIB9BfqG7kIGDPTHHgbrzpEbgNvUpPt/l74mF5rQ5UW26VRp3A==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@netlify/ai/-/ai-0.3.5.tgz", + "integrity": "sha512-7suwHOBy9s14yeWRxt+w3Zh6Rrx8gX7zP/xmsxqxLyJlcBykWm6siBJs2mMtJgbWvcrgI5BEgNLh5qfXlTCsRQ==", "dev": true, "dependencies": { - "@netlify/api": "^14.0.10" + "@netlify/api": "^14.0.12" }, "engines": { "node": ">=20.6.1" }, "peerDependencies": { - "@netlify/api": ">=14.0.10" + "@netlify/api": ">=14.0.12" } }, "node_modules/@netlify/api": { - "version": "14.0.10", - "resolved": "https://registry.npmjs.org/@netlify/api/-/api-14.0.10.tgz", - "integrity": "sha512-2zPdZzayOGMff/atHyNE8y060R/PuWfvsFzywe9cW2hLzfJtYy1+hFFrfZS6a1KL5RkqsAFd8OBEO0s+ospDWA==", + "version": "14.0.12", + "resolved": "https://registry.npmjs.org/@netlify/api/-/api-14.0.12.tgz", + "integrity": "sha512-4xSfHAj9PIZZ78YOPby6TBHxYnf6sOE1/jpkHSDyt2oRxF94qJ0fhp96Fo2kq/rIhvgTlU5Ce3HARi8BDY4mLw==", "dev": true, "license": "MIT", "dependencies": { - "@netlify/open-api": "^2.43.1", + "@netlify/open-api": "^2.45.0", "node-fetch": "^3.0.0", "p-wait-for": "^5.0.0", "picoquery": "^2.5.0" @@ -2279,14 +2279,14 @@ "license": "Apache 2" }, "node_modules/@netlify/blobs": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-10.4.1.tgz", - "integrity": "sha512-43wntITwgocQxJThPxtgZ9XCPk2wAXrqD61uqgcIG2EhT/cXpvfEGEo6nLXXtb9irW6nQVXusJeLHEhKJFC4Vg==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/@netlify/blobs/-/blobs-10.4.3.tgz", + "integrity": "sha512-a5Wh4Mc6OwR4vUPnp4DAySDugvrrUmA16NEW0atUnP9R2kOr7vAmV986MXzQGpb57w3B8aq3XXuG/jquTvciiQ==", "dev": true, "license": "MIT", "dependencies": { - "@netlify/dev-utils": "4.3.2", - "@netlify/otel": "^5.0.0", + "@netlify/dev-utils": "4.3.3", + "@netlify/otel": "^5.1.0", "@netlify/runtime-utils": "2.2.1" }, "engines": { @@ -2294,9 +2294,9 @@ } }, "node_modules/@netlify/cache": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@netlify/cache/-/cache-3.3.3.tgz", - "integrity": "sha512-xWiKDtGSqIUYev8LVtdeBsDFLjczlvn8tp4nMs+3QqPk7FJ94j7qwFjB+QRWg7g4Fbin9UvgIrMlmAEXE+gjKA==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@netlify/cache/-/cache-3.3.4.tgz", + "integrity": "sha512-Fl4/KxP8NS7+skjfRixgekuqBNvLPay/J6qC2mxvHjkkZNu1oUs8QOc+T3Nvt4n+UMrltnt9ggg0q/q4hmBIVw==", "dev": true, "license": "MIT", "dependencies": { @@ -2307,16 +2307,16 @@ } }, "node_modules/@netlify/config": { - "version": "23.2.0", - "resolved": "https://registry.npmjs.org/@netlify/config/-/config-23.2.0.tgz", - "integrity": "sha512-zlI792/efPUY1XKtBML2OJBgKMyfNQIeGEYibH8SqeDxPjNuCy0qELE0U9Sc6+Ss34XryPBUPdV60tYhSoe6lw==", + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@netlify/config/-/config-24.2.0.tgz", + "integrity": "sha512-idc1D6kdQOFjG70aZC06crqElTyaSulVlnOEDZX2+5/vcmfFCBu8CJSEd5YzC6VCCXBgOW3Hw0cVxDTl5X6+CQ==", "dev": true, "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", - "@netlify/api": "^14.0.3", - "@netlify/headers-parser": "^9.0.1", - "@netlify/redirect-parser": "^15.0.2", + "@netlify/api": "^14.0.12", + "@netlify/headers-parser": "^9.0.2", + "@netlify/redirect-parser": "^15.0.3", "chalk": "^5.0.0", "cron-parser": "^4.1.0", "deepmerge": "^4.2.2", @@ -2336,7 +2336,8 @@ "tomlify-j0.4": "^3.0.0", "validate-npm-package-name": "^5.0.0", "yaml": "^2.8.0", - "yargs": "^17.6.0" + "yargs": "^17.6.0", + "zod": "^4.0.5" }, "bin": { "netlify-config": "bin.js" @@ -2461,23 +2462,23 @@ } }, "node_modules/@netlify/dev": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/@netlify/dev/-/dev-4.8.2.tgz", - "integrity": "sha512-XY+TylQZwqm0mDHz4N6ovSAWE0T/dkjMu7y/yr7idG12hMscfr4dOvkLEn5LiueMrbneAwpte1kkNcygRa0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@netlify/ai": "^0.3.3", - "@netlify/blobs": "10.4.1", - "@netlify/config": "^23.2.0", - "@netlify/dev-utils": "4.3.2", - "@netlify/edge-functions-dev": "1.0.5", - "@netlify/functions-dev": "1.1.2", - "@netlify/headers": "2.1.2", - "@netlify/images": "1.3.2", - "@netlify/redirects": "3.1.3", - "@netlify/runtime": "4.1.9", - "@netlify/static": "3.1.2", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/@netlify/dev/-/dev-4.8.4.tgz", + "integrity": "sha512-YGmsIvv0JeL8ojbyr9XWXWj3FNAIImxhppFN9WhRZyK0w5fDC6bVlNRQsqApPxd1QVDMfTQFY4aYQ0ratE0nUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/ai": "^0.3.5", + "@netlify/blobs": "10.4.3", + "@netlify/config": "^24.1.2", + "@netlify/dev-utils": "4.3.3", + "@netlify/edge-functions-dev": "1.0.7", + "@netlify/functions-dev": "1.1.4", + "@netlify/headers": "2.1.3", + "@netlify/images": "1.3.3", + "@netlify/redirects": "3.1.4", + "@netlify/runtime": "4.1.11", + "@netlify/static": "3.1.3", "ulid": "^3.0.0" }, "engines": { @@ -2485,9 +2486,9 @@ } }, "node_modules/@netlify/dev-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@netlify/dev-utils/-/dev-utils-4.3.2.tgz", - "integrity": "sha512-Nl6c5UVLbpOwvzVaT6fJycdkc3EswqFoI9c2hZ3WUUX+kQ2ojdrkFMuKcPERaGXYxrhy/uGk1CURAflG8YC2RA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@netlify/dev-utils/-/dev-utils-4.3.3.tgz", + "integrity": "sha512-qziF8R9kf7mRNgSpmUH96O0aV1ZiwK4c9ZecFQbDSQuYhgy9GY1WTjiQF0oQnohjTjWNtXhrU39LAeXWNLaBJg==", "dev": true, "license": "MIT", "dependencies": { @@ -2555,9 +2556,9 @@ } }, "node_modules/@netlify/edge-bundler": { - "version": "14.9.0", - "resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-14.9.0.tgz", - "integrity": "sha512-EyEzPiVH8xubRQrxyp9j9aZLOkg3WsfMabgQlXMhPnh4I78ZykjVHiERFr71bSvWQH1GE5sBLoATSgmjLJv5yw==", + "version": "14.9.1", + "resolved": "https://registry.npmjs.org/@netlify/edge-bundler/-/edge-bundler-14.9.1.tgz", + "integrity": "sha512-EFkoPlCqBnoFKo8XyTw9nI91DBQAhms398tXKThecAXI/smrHiv4/sGYXj2ISHR022Aoioll8K9U6aLvmQgDAg==", "dev": true, "license": "MIT", "dependencies": { @@ -2567,7 +2568,7 @@ "better-ajv-errors": "^1.2.0", "common-path-prefix": "^3.0.0", "env-paths": "^3.0.0", - "esbuild": "0.25.11", + "esbuild": "0.27.1", "execa": "^8.0.0", "find-up": "^7.0.0", "get-port": "^7.0.0", @@ -2586,504 +2587,1430 @@ "node": ">=18.14.0" } }, - "node_modules/@netlify/edge-bundler/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@netlify/edge-bundler/node_modules/ajv-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", - "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.1" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@netlify/edge-bundler/node_modules/find-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", - "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "locate-path": "^7.2.0", - "path-exists": "^5.0.0", - "unicorn-magic": "^0.1.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@netlify/edge-bundler/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@netlify/edge-bundler/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@netlify/edge-bundler/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@netlify/edge-bundler/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "p-limit": "^4.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@netlify/edge-bundler/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@netlify/edge-bundler/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@netlify/edge-bundler/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "cpu": [ + "arm" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/@netlify/edge-bundler/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "cpu": [ + "arm64" ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/@netlify/edge-bundler/node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@netlify/edge-functions": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@netlify/edge-functions/-/edge-functions-3.0.2.tgz", - "integrity": "sha512-1vW3R+Rc2JxL6qITndlT87N94GPjJ6gH2ntXW3IDdLzSABoU9XCHw4lRzDw+bhgSLTm0oyOwQA2+hhFvstznNQ==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@netlify/types": "2.2.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@netlify/edge-functions-bootstrap": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@netlify/edge-functions-bootstrap/-/edge-functions-bootstrap-2.16.0.tgz", - "integrity": "sha512-v8QQihSbBHj3JxtJsHoepXALpNumD9M7egHoc8z62FYl5it34dWczkaJoFFopEyhiBVKi4K/n0ZYpdzwfujd6g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@netlify/edge-functions-dev": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@netlify/edge-functions-dev/-/edge-functions-dev-1.0.5.tgz", - "integrity": "sha512-rgpNmMq4oGGvVs4Rkqtwl1NJE7dV0ki+EXz98ReVo20snCam60iXkGKLoLtKtQzFg1e0pkz+UNq5CafC4zFTNw==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@netlify/dev-utils": "4.3.2", - "@netlify/edge-bundler": "^14.8.7", - "@netlify/edge-functions": "3.0.2", - "@netlify/edge-functions-bootstrap": "2.16.0", - "@netlify/runtime-utils": "2.2.1", - "get-port": "^7.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=20.6.1" + "node": ">=18" } }, - "node_modules/@netlify/functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-5.1.0.tgz", - "integrity": "sha512-LZtiQtf/QzPHIeNDZuIBxx04kmU7lCipWqZ26ejX7mYSB3yj2wvpZfF49kD8B8FoKTydSvgFmBpIcCO5FvpEXA==", + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", - "dependencies": { - "@netlify/types": "2.2.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@netlify/functions-dev": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@netlify/functions-dev/-/functions-dev-1.1.2.tgz", - "integrity": "sha512-O3Exq9yomIBsar7Yv7MkJ3qHQNLy3gAumuqzQ5DVyf7pMfNpyPmZCPF+bdVQdIGt5qrhNtUZYL2VIZgf+YOVgg==", - "dev": true, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/ajv-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", + "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.1" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@netlify/edge-bundler/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/@netlify/edge-bundler/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@netlify/edge-functions": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@netlify/edge-functions/-/edge-functions-3.0.3.tgz", + "integrity": "sha512-grElRK+rTBdYrPsULPKrhcHhrW+fwpDRLPbGByqa6Xrz0fhzcFJ2D9ijxEQ/onFcSVPYHT1u1mI48GhS5bZ/Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/types": "2.3.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@netlify/edge-functions-bootstrap": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@netlify/edge-functions-bootstrap/-/edge-functions-bootstrap-2.16.0.tgz", + "integrity": "sha512-v8QQihSbBHj3JxtJsHoepXALpNumD9M7egHoc8z62FYl5it34dWczkaJoFFopEyhiBVKi4K/n0ZYpdzwfujd6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@netlify/edge-functions-dev": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@netlify/edge-functions-dev/-/edge-functions-dev-1.0.7.tgz", + "integrity": "sha512-PlkG3PxULQ7z/CSzx5LthGsVtJPOo8E+sA67cOwNq/eHxtwpCUfCPOmxq3AGKqMR1pzUGC6k5yewhgXoG8Zm7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/dev-utils": "4.3.3", + "@netlify/edge-bundler": "^14.9.1", + "@netlify/edge-functions": "3.0.3", + "@netlify/edge-functions-bootstrap": "2.16.0", + "@netlify/runtime-utils": "2.2.1", + "get-port": "^7.1.0" + }, + "engines": { + "node": ">=20.6.1" + } + }, + "node_modules/@netlify/functions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-5.1.1.tgz", + "integrity": "sha512-64TvwQkAFpYb3QqYemPYDqWi1xMbYOBfg70bhy23iahWf+F9TJgOOnAVUOk5fMWGN/fk9bZG5ROc+cm32whh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/types": "2.3.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@netlify/functions-dev": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@netlify/functions-dev/-/functions-dev-1.1.4.tgz", + "integrity": "sha512-L7+yFgDrG+pTAsLo4wZZAEV+2m3BiEocjdINEMdXvjVikW9PH3anDXtDJqtDd6kXl9G/o4fd9crBaeN97V+k2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/blobs": "10.4.3", + "@netlify/dev-utils": "4.3.3", + "@netlify/functions": "5.1.1", + "@netlify/zip-it-and-ship-it": "^14.1.15", + "cron-parser": "^4.9.0", + "decache": "^4.6.2", + "extract-zip": "^2.0.1", + "is-stream": "^4.0.1", + "jwt-decode": "^4.0.0", + "lambda-local": "^2.2.0", + "read-package-up": "^11.0.0", + "semver": "^7.6.3", + "source-map-support": "^0.5.21" + }, + "engines": { + "node": ">=20.6.1" + } + }, + "node_modules/@netlify/functions-dev/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@netlify/headers": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@netlify/headers/-/headers-2.1.3.tgz", + "integrity": "sha512-jVjhHokAQLGI5SJA2nj8OWeNQ7ASV4m0n4aiR4PHrhM8ot385V2BbUGkSpC28M92uqP0l1cbAQaSoSOU4re8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/headers-parser": "^9.0.2" + }, + "engines": { + "node": ">=20.6.1" + } + }, + "node_modules/@netlify/headers-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@netlify/headers-parser/-/headers-parser-9.0.2.tgz", + "integrity": "sha512-86YEGPxVemhksY1LeSr8NSOyH11RHvYHq+FuBJnTlPZoRDX+TD+0TAxF6lwzAgVTd1VPkyFEHlNgUGqw7aNzRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "escape-string-regexp": "^5.0.0", + "fast-safe-stringify": "^2.0.7", + "is-plain-obj": "^4.0.0", + "map-obj": "^5.0.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": ">=18.14.0" + } + }, + "node_modules/@netlify/headers-parser/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@netlify/images": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@netlify/images/-/images-1.3.3.tgz", + "integrity": "sha512-1X3fUmacCLMlPIqyeV5tdo6Wbf9aBSWobgr4DyRvg9zDV9jbKqgdN3BNbcUXmVaqfN+0iiv0k9p02mcRV3OyOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ipx": "^3.1.1" + }, + "engines": { + "node": ">=20.6.1" + } + }, + "node_modules/@netlify/open-api": { + "version": "2.45.0", + "resolved": "https://registry.npmjs.org/@netlify/open-api/-/open-api-2.45.0.tgz", + "integrity": "sha512-kLysr2N8HQi0qoEq04vpRvrE/fSnZaXJYf1bVxKre2lLaM1RSm05hqDswKTgxM601pZf9h1i1Ea3L4DZNgHb5w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.8.0" + } + }, + "node_modules/@netlify/otel": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-5.1.0.tgz", + "integrity": "sha512-bXZuWXXq3IZQ8i3/b5YD7ud5fQq/fiiCR51ClUx3WPcDbH/Rck0F6gc08yyrB5+8Dn8IYmvj+xlWL23B39YDHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "1.9.0", + "@opentelemetry/core": "1.30.1", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/sdk-trace-node": "1.30.1" + }, + "engines": { + "node": "^18.14.0 || >=20.6.1" + } + }, + "node_modules/@netlify/redirect-parser": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@netlify/redirect-parser/-/redirect-parser-15.0.3.tgz", + "integrity": "sha512-/HB3fcRRNgf6O/pbLn4EYNDHrU2kiadMMnazg8/OjvQK2S9i4y61vQcrICvDxYKUKQdgeEaABUuaCNAJFnfD9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "fast-safe-stringify": "^2.1.1", + "is-plain-obj": "^4.0.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": ">=18.14.0" + } + }, + "node_modules/@netlify/redirect-parser/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@netlify/redirects": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@netlify/redirects/-/redirects-3.1.4.tgz", + "integrity": "sha512-2FcF/0Q24JA+VmpWlVRp835UvhBHQe3XGVaxAQfHiDd5aXztaz2U5Y4VEZyrZJOubY5xnxr2yqumDfClAiCKxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/dev-utils": "4.3.3", + "@netlify/redirect-parser": "^15.0.3", + "cookie": "^1.0.2", + "jsonwebtoken": "9.0.2", + "netlify-redirector": "^0.5.0" + }, + "engines": { + "node": ">=20.6.1" + } + }, + "node_modules/@netlify/runtime": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@netlify/runtime/-/runtime-4.1.11.tgz", + "integrity": "sha512-tfqZmNH3pm4E9KoWKP/H5d4+Acl/25m/PXTh5EDIR1iRQ8ZyJkAJlnjtjCaSg2u6leAFkbOrdeTMLnvFenF75g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@netlify/blobs": "^10.4.3", + "@netlify/cache": "3.3.4", + "@netlify/runtime-utils": "2.2.1", + "@netlify/types": "2.3.0" + }, + "engines": { + "node": ">=20.6.1" + } + }, + "node_modules/@netlify/runtime-utils": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@netlify/runtime-utils/-/runtime-utils-2.2.1.tgz", + "integrity": "sha512-dyJeuggzQM8+Dsi0T8Z9UjfLJ6vCmNC36W6WE2aqzfTdTw4wPkh2xlEu4LoD75+TGuYK7jIhEoU2QcCXOzfyAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || >=20" + } + }, + "node_modules/@netlify/serverless-functions-api": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-2.8.1.tgz", + "integrity": "sha512-UZVlpSCgBS/6gdamRlAlOkRV3Atd6BUgpU6n59JdKsfAj67z2XbiGn7dWZuOaCrdv3h4FX3Z5zHZsSDRDi5V6g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@netlify/static": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@netlify/static/-/static-3.1.3.tgz", + "integrity": "sha512-88VG2jwWY1eOT/IiMbkrak7qyo+t7om0v731i63JiCDfXjCEp+yFPNr9L4v8S6wcCmgnkGQ6Sr5roF1sEtp6+Q==", + "dev": true, "license": "MIT", "dependencies": { - "@netlify/blobs": "10.4.1", - "@netlify/dev-utils": "4.3.2", - "@netlify/functions": "5.1.0", - "@netlify/zip-it-and-ship-it": "^14.1.13", - "cron-parser": "^4.9.0", - "decache": "^4.6.2", - "extract-zip": "^2.0.1", - "is-stream": "^4.0.1", - "jwt-decode": "^4.0.0", - "lambda-local": "^2.2.0", - "read-package-up": "^11.0.0", - "semver": "^7.6.3", - "source-map-support": "^0.5.21" + "mime-types": "^3.0.0" }, "engines": { "node": ">=20.6.1" } }, - "node_modules/@netlify/functions-dev/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "node_modules/@netlify/types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@netlify/types/-/types-2.3.0.tgz", + "integrity": "sha512-5gxMWh/S7wr0uHKSTbMv4bjWmWSpwpeLYvErWeVNAPll5/QNFo9aWimMAUuh8ReLY3/fg92XAroVVu7+z27Snw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": "^18.14.0 || >=20" } }, - "node_modules/@netlify/headers": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@netlify/headers/-/headers-2.1.2.tgz", - "integrity": "sha512-3dkP1LU9U3ynKHuLUP0HCzJhf0bLs/ESuz1QjnUTOwx6oxc3hkIFa8B8wAa00uOrx3lVgcN76Zuydvkjulk7wA==", + "node_modules/@netlify/vite-plugin": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@netlify/vite-plugin/-/vite-plugin-2.7.16.tgz", + "integrity": "sha512-PdpKYobM0oH7Qh87Aao4z5drNcBwq9ahkom5ep6vEdh5koPDKCBY+334gB9nIihy3pmruc3ur6+3jRyKjAkm1w==", "dev": true, "license": "MIT", "dependencies": { - "@netlify/headers-parser": "^9.0.2" + "@netlify/dev": "4.8.4", + "@netlify/dev-utils": "^4.3.3", + "dedent": "^1.7.0" }, "engines": { - "node": ">=20.6.1" + "node": "^20.6.1 || >=22" + }, + "peerDependencies": { + "vite": "^5 || ^6 || ^7" } }, - "node_modules/@netlify/headers-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@netlify/headers-parser/-/headers-parser-9.0.2.tgz", - "integrity": "sha512-86YEGPxVemhksY1LeSr8NSOyH11RHvYHq+FuBJnTlPZoRDX+TD+0TAxF6lwzAgVTd1VPkyFEHlNgUGqw7aNzRQ==", + "node_modules/@netlify/zip-it-and-ship-it": { + "version": "14.1.16", + "resolved": "https://registry.npmjs.org/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-14.1.16.tgz", + "integrity": "sha512-oUliNza9Tab3hHpIaHDAuT6ApN/c3SPJDUUenFGujpJm6IKZPdmnAGuKM2N2zb+XxHzApLdOogqv0kr7nY0avQ==", "dev": true, "license": "MIT", "dependencies": { - "@iarna/toml": "^2.2.5", - "escape-string-regexp": "^5.0.0", - "fast-safe-stringify": "^2.0.7", - "is-plain-obj": "^4.0.0", - "map-obj": "^5.0.0", - "path-exists": "^5.0.0" + "@babel/parser": "^7.22.5", + "@babel/types": "7.28.5", + "@netlify/binary-info": "^1.0.0", + "@netlify/serverless-functions-api": "^2.8.1", + "@vercel/nft": "0.29.4", + "archiver": "^7.0.0", + "common-path-prefix": "^3.0.0", + "copy-file": "^11.0.0", + "es-module-lexer": "^1.0.0", + "esbuild": "0.27.1", + "execa": "^8.0.0", + "fast-glob": "^3.3.3", + "filter-obj": "^6.0.0", + "find-up": "^7.0.0", + "is-path-inside": "^4.0.0", + "junk": "^4.0.0", + "locate-path": "^7.0.0", + "merge-options": "^3.0.4", + "minimatch": "^9.0.0", + "normalize-path": "^3.0.0", + "p-map": "^7.0.0", + "path-exists": "^5.0.0", + "precinct": "^12.0.0", + "require-package-name": "^2.0.1", + "resolve": "^2.0.0-next.1", + "semver": "^7.3.8", + "tmp-promise": "^3.0.2", + "toml": "^3.0.0", + "unixify": "^1.0.0", + "urlpattern-polyfill": "8.0.2", + "yargs": "^17.0.0", + "zod": "^3.23.8" + }, + "bin": { + "zip-it-and-ship-it": "bin.js" }, "engines": { "node": ">=18.14.0" } }, - "node_modules/@netlify/headers-parser/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@netlify/images": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@netlify/images/-/images-1.3.2.tgz", - "integrity": "sha512-/YEhpm0KbiDtbYZbfOv6tbJyk+/j10tndeJAvUc94mSyv3EB7X4DREvhftZcxLuE6A2IceaKIbh5DpUNgJHjxA==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "ipx": "^3.1.1" - }, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=20.6.1" + "node": ">=18" } }, - "node_modules/@netlify/open-api": { - "version": "2.43.1", - "resolved": "https://registry.npmjs.org/@netlify/open-api/-/open-api-2.43.1.tgz", - "integrity": "sha512-MPhzLfVVTzQCs9iIjxxgIoXv6/tE2FVHTFT2gi4leChj5o4DQ9an/6gc1q7QPRXeIuPIb+P6AaYi3TGHf8vsBA==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14.8.0" + "node": ">=18" } }, - "node_modules/@netlify/otel": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@netlify/otel/-/otel-5.0.0.tgz", - "integrity": "sha512-7EWbS+puDub800IQ9MUVcLrWwCNPyK/u1Rs08f0Y+O4dBGVkuTm/RyaLoU58PPLuNCfPHXebfIYFZxN+/CtZeA==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "@opentelemetry/api": "1.9.0", - "@opentelemetry/core": "1.30.1", - "@opentelemetry/instrumentation": "^0.203.0", - "@opentelemetry/resources": "1.30.1", - "@opentelemetry/sdk-trace-node": "1.30.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.14.0 || >=20.6.1" + "node": ">=18" } }, - "node_modules/@netlify/redirect-parser": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@netlify/redirect-parser/-/redirect-parser-15.0.3.tgz", - "integrity": "sha512-/HB3fcRRNgf6O/pbLn4EYNDHrU2kiadMMnazg8/OjvQK2S9i4y61vQcrICvDxYKUKQdgeEaABUuaCNAJFnfD9w==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@iarna/toml": "^2.2.5", - "fast-safe-stringify": "^2.1.1", - "is-plain-obj": "^4.0.0", - "path-exists": "^5.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.14.0" + "node": ">=18" } }, - "node_modules/@netlify/redirect-parser/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@netlify/redirects": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@netlify/redirects/-/redirects-3.1.3.tgz", - "integrity": "sha512-gIk/h4nzg9n/LWV7odfqMP00MBRSfujnW50DRYkWqh4ApW5NdfZxOcla7ISl6hZcIDMUruOPcFOD7PFXSALVvQ==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@netlify/dev-utils": "4.3.2", - "@netlify/redirect-parser": "^15.0.3", - "cookie": "^1.0.2", - "jsonwebtoken": "9.0.2", - "netlify-redirector": "^0.5.0" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=20.6.1" + "node": ">=18" } }, - "node_modules/@netlify/runtime": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@netlify/runtime/-/runtime-4.1.9.tgz", - "integrity": "sha512-q+kg+1s7MZL3GMDY6KwsWxpFLmkk7JL3+wRrLKIrDh+CBMPBOMnGV1uLfT8ndOHf7sicvMf8xLIlv/gzZt7B8w==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@netlify/blobs": "^10.4.1", - "@netlify/cache": "3.3.3", - "@netlify/runtime-utils": "2.2.1", - "@netlify/types": "2.2.0" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=20.6.1" + "node": ">=18" } }, - "node_modules/@netlify/runtime-utils": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@netlify/runtime-utils/-/runtime-utils-2.2.1.tgz", - "integrity": "sha512-dyJeuggzQM8+Dsi0T8Z9UjfLJ6vCmNC36W6WE2aqzfTdTw4wPkh2xlEu4LoD75+TGuYK7jIhEoU2QcCXOzfyAQ==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^18.14.0 || >=20" + "node": ">=18" } }, - "node_modules/@netlify/serverless-functions-api": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-2.7.2.tgz", - "integrity": "sha512-/hevTzZMi0kZdclzfoIAd+UfXcYG/E9CjIiAqy6mFN0sSjeHdUO0v6P8GF2heVtQQzUyMBxebMmAzUVt5TsbXg==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@netlify/static": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@netlify/static/-/static-3.1.2.tgz", - "integrity": "sha512-1kxT/xTro9+zzdbyxdNSZpzwzOwlxBTbLPpVsuvF+77euman6Fxhwyt9KoEBMSwO3dyKC3LW9W4Wa/k+zeDqpg==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=20.6.1" + "node": ">=18" } }, - "node_modules/@netlify/types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@netlify/types/-/types-2.2.0.tgz", - "integrity": "sha512-XOWlZ2wPpdRKkAOcQbjIf/Qz7L4RjcSVINVNQ9p3F6U8V6KSEOsB3fPrc6Ly8EOeJioHUepRPuzHzJE/7V5EsA==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^18.14.0 || >=20" + "node": ">=18" } }, - "node_modules/@netlify/vite-plugin": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/@netlify/vite-plugin/-/vite-plugin-2.7.14.tgz", - "integrity": "sha512-Vf9fljPa+3CVOtCFFmuAQjHEQ6WuYGHs1Gcs2nOVS626zL96Hu4vXvYztndJx6R4cO9uWD8NQPtvpHWZLdQNzA==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@netlify/dev": "4.8.2", - "@netlify/dev-utils": "^4.3.2", - "dedent": "^1.7.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^20.6.1 || >=22" - }, - "peerDependencies": { - "vite": "^5 || ^6 || ^7" + "node": ">=18" } }, - "node_modules/@netlify/zip-it-and-ship-it": { - "version": "14.1.14", - "resolved": "https://registry.npmjs.org/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-14.1.14.tgz", - "integrity": "sha512-33w50VcYLZ7RpUCFvl+n8JoLRGSVKerbH6cXtVjzA7un9JSkJWZQVS3nDmWYbq6OR0VnS1LGn7r+/ll6pSOvCg==", + "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.22.5", - "@babel/types": "7.28.5", - "@netlify/binary-info": "^1.0.0", - "@netlify/serverless-functions-api": "^2.7.2", - "@vercel/nft": "0.29.4", - "archiver": "^7.0.0", - "common-path-prefix": "^3.0.0", - "copy-file": "^11.0.0", - "es-module-lexer": "^1.0.0", - "esbuild": "0.25.11", - "execa": "^8.0.0", - "fast-glob": "^3.3.3", - "filter-obj": "^6.0.0", - "find-up": "^7.0.0", - "is-path-inside": "^4.0.0", - "junk": "^4.0.0", - "locate-path": "^7.0.0", - "merge-options": "^3.0.4", - "minimatch": "^9.0.0", - "normalize-path": "^3.0.0", - "p-map": "^7.0.0", - "path-exists": "^5.0.0", - "precinct": "^12.0.0", - "require-package-name": "^2.0.1", - "resolve": "^2.0.0-next.1", - "semver": "^7.3.8", - "tmp-promise": "^3.0.2", - "toml": "^3.0.0", - "unixify": "^1.0.0", - "urlpattern-polyfill": "8.0.2", - "yargs": "^17.0.0", - "zod": "^3.23.8" - }, - "bin": { - "zip-it-and-ship-it": "bin.js" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=18.14.0" + "node": ">=18" } }, "node_modules/@netlify/zip-it-and-ship-it/node_modules/brace-expansion": { @@ -3096,6 +4023,48 @@ "balanced-match": "^1.0.0" } }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, "node_modules/@netlify/zip-it-and-ship-it/node_modules/find-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", @@ -3232,11 +4201,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@netlify/zip-it-and-ship-it/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -3250,6 +4230,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -3259,6 +4240,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -4621,9 +5603,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.90.11", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.11.tgz", - "integrity": "sha512-f9z/nXhCgWDF4lHqgIE30jxLe4sYv15QodfdPDKYAk7nAEjNcndy4dHz3ezhdUaR23BpWa4I2EH4/DZ0//Uf8A==", + "version": "5.90.12", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.12.tgz", + "integrity": "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg==", "license": "MIT", "funding": { "type": "github", @@ -4631,12 +5613,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.90.11", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.11.tgz", - "integrity": "sha512-3uyzz01D1fkTLXuxF3JfoJoHQMU2fxsfJwE+6N5hHy0dVNoZOvwKP8Z2k7k1KDeD54N20apcJnG75TBAStIrBA==", + "version": "5.90.12", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.12.tgz", + "integrity": "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.90.11" + "@tanstack/query-core": "5.90.12" }, "funding": { "type": "github", @@ -4950,18 +5932,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.0.tgz", - "integrity": "sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", + "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.48.0", - "@typescript-eslint/type-utils": "8.48.0", - "@typescript-eslint/utils": "8.48.0", - "@typescript-eslint/visitor-keys": "8.48.0", - "graphemer": "^1.4.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/type-utils": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" @@ -4974,7 +5955,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.48.0", + "@typescript-eslint/parser": "^8.49.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -4990,16 +5971,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.0.tgz", - "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", + "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.48.0", - "@typescript-eslint/types": "8.48.0", - "@typescript-eslint/typescript-estree": "8.48.0", - "@typescript-eslint/visitor-keys": "8.48.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4" }, "engines": { @@ -5015,14 +5996,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.0.tgz", - "integrity": "sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz", + "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.48.0", - "@typescript-eslint/types": "^8.48.0", + "@typescript-eslint/tsconfig-utils": "^8.49.0", + "@typescript-eslint/types": "^8.49.0", "debug": "^4.3.4" }, "engines": { @@ -5037,14 +6018,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.0.tgz", - "integrity": "sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz", + "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.0", - "@typescript-eslint/visitor-keys": "8.48.0" + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5055,9 +6036,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.0.tgz", - "integrity": "sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz", + "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==", "dev": true, "license": "MIT", "engines": { @@ -5072,15 +6053,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.0.tgz", - "integrity": "sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz", + "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.0", - "@typescript-eslint/typescript-estree": "8.48.0", - "@typescript-eslint/utils": "8.48.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -5097,9 +6078,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.0.tgz", - "integrity": "sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", + "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", "dev": true, "license": "MIT", "engines": { @@ -5111,16 +6092,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.0.tgz", - "integrity": "sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz", + "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.48.0", - "@typescript-eslint/tsconfig-utils": "8.48.0", - "@typescript-eslint/types": "8.48.0", - "@typescript-eslint/visitor-keys": "8.48.0", + "@typescript-eslint/project-service": "8.49.0", + "@typescript-eslint/tsconfig-utils": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", @@ -5178,16 +6159,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.0.tgz", - "integrity": "sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz", + "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.48.0", - "@typescript-eslint/types": "8.48.0", - "@typescript-eslint/typescript-estree": "8.48.0" + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5202,13 +6183,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.0.tgz", - "integrity": "sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz", + "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/types": "8.49.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5688,14 +6669,14 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", - "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz", + "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.24", + "@vue/shared": "3.5.25", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -5722,28 +6703,28 @@ "license": "MIT" }, "node_modules/@vue/compiler-dom": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", - "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", + "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.24", - "@vue/shared": "3.5.24" + "@vue/compiler-core": "3.5.25", + "@vue/shared": "3.5.25" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", - "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", + "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", - "@vue/compiler-core": "3.5.24", - "@vue/compiler-dom": "3.5.24", - "@vue/compiler-ssr": "3.5.24", - "@vue/shared": "3.5.24", + "@vue/compiler-core": "3.5.25", + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", @@ -5758,20 +6739,20 @@ "license": "MIT" }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", - "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", + "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.24", - "@vue/shared": "3.5.24" + "@vue/compiler-dom": "3.5.25", + "@vue/shared": "3.5.25" } }, "node_modules/@vue/shared": { - "version": "3.5.24", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", - "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", + "version": "3.5.25", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.25.tgz", + "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==", "dev": true, "license": "MIT" }, @@ -8880,6 +9861,7 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -8896,6 +9878,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -8945,6 +9928,7 @@ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -9481,13 +10465,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/graphql": { "version": "16.12.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", @@ -10823,13 +11800,13 @@ } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "dev": true, "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -11739,6 +12716,7 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -12311,6 +13289,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -12477,9 +13456,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/msw": { - "version": "2.12.3", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.3.tgz", - "integrity": "sha512-/5rpGC0eK8LlFqsHaBmL19/PVKxu/CCt8pO1vzp9X6SDLsRDh/Ccudkf3Ur5lyaKxJz9ndAx+LaThdv0ySqB6A==", + "version": "2.12.4", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.4.tgz", + "integrity": "sha512-rHNiVfTyKhzc0EjoXUBVGteNKBevdjOlVC6GlIRXpy+/3LHEIGRovnB5WPjcvmNODVQ1TNFnoa7wsGbd0V3epg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -12649,9 +13628,9 @@ "license": "MIT" }, "node_modules/node-forge": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", - "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { @@ -12671,9 +13650,9 @@ } }, "node_modules/node-mock-http": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.3.tgz", - "integrity": "sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", "dev": true, "license": "MIT" }, @@ -13731,7 +14710,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/quote-unquote": { "version": "1.0.0", @@ -14188,6 +15168,7 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -14252,6 +15233,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -15609,16 +16591,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.0.tgz", - "integrity": "sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz", + "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.48.0", - "@typescript-eslint/parser": "8.48.0", - "@typescript-eslint/typescript-estree": "8.48.0", - "@typescript-eslint/utils": "8.48.0" + "@typescript-eslint/eslint-plugin": "8.49.0", + "@typescript-eslint/parser": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -15640,9 +16622,9 @@ "license": "MIT" }, "node_modules/ulid": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ulid/-/ulid-3.0.1.tgz", - "integrity": "sha512-dPJyqPzx8preQhqq24bBG1YNkvigm87K8kVEHCD+ruZg24t6IFEFv00xMWfxcC4djmFtiTLdFuADn4+DOz6R7Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-3.0.2.tgz", + "integrity": "sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w==", "dev": true, "license": "MIT", "bin": { @@ -16141,9 +17123,9 @@ } }, "node_modules/vite": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", - "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", + "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16573,9 +17555,9 @@ } }, "node_modules/winston": { - "version": "3.18.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", - "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", "dev": true, "license": "MIT", "dependencies": { @@ -17014,9 +17996,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "dev": true, "license": "MIT", "funding": { diff --git a/web/package.json b/web/package.json index 9d5e1bb45f..b45d058e6c 100644 --- a/web/package.json +++ b/web/package.json @@ -22,7 +22,7 @@ "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.17", "@tailwindcss/typography": "^0.5.19", - "@tanstack/react-query": "^5.90.11", + "@tanstack/react-query": "^5.90.12", "lucide-react": "^0.556.0", "openapi-fetch": "^0.15.0", "react": "^19.1.2", @@ -35,7 +35,7 @@ "@eslint/js": "^9.39.1", "@faker-js/faker": "^10.1.0", "@netlify/functions": "^5.0.1", - "@netlify/vite-plugin": "^2.7.14", + "@netlify/vite-plugin": "^2.7.15", "@tailwindcss/vite": "^4.1.17", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", @@ -52,14 +52,14 @@ "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", "jsdom": "^27.1.0", - "msw": "2.12.3", + "msw": "2.12.4", "openapi-backend": "^5.15.0", "openapi-typescript": "^7.10.1", "tailwindcss": "^4.1.13", "typescript": "^5.9.3", - "typescript-eslint": "^8.48.0", + "typescript-eslint": "^8.48.1", "unified": "^11.0.5", - "vite": "^7.2.4", + "vite": "^7.2.6", "vite-plugin-static-copy": "^3.1.4", "vitest": "^3.2.4" } From 5641c43f8f8cfe90312fbbe189877d68da820c6b Mon Sep 17 00:00:00 2001 From: Ethan Mosbaugh Date: Thu, 11 Dec 2025 11:38:57 -0800 Subject: [PATCH 07/11] fix err scope --- api/controllers/app/install.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/controllers/app/install.go b/api/controllers/app/install.go index e1d3b5408b..21405acbcd 100644 --- a/api/controllers/app/install.go +++ b/api/controllers/app/install.go @@ -112,8 +112,7 @@ func (c *AppController) InstallApp(ctx context.Context, opts InstallAppOptions) } // Install the app with installable charts - err = c.appInstallManager.Install(ctx, installableCharts, appConfigValues, opts.RegistrySettings, opts.HostCABundlePath) - if err != nil { + if err := c.appInstallManager.Install(ctx, installableCharts, appConfigValues, opts.RegistrySettings, opts.HostCABundlePath); err != nil { return fmt.Errorf("install app: %w", err) } From def00831c91f77ea6a183584bfe6c7ed9a46934a Mon Sep 17 00:00:00 2001 From: Ethan Mosbaugh Date: Thu, 11 Dec 2025 11:39:44 -0800 Subject: [PATCH 08/11] do not support watching namespaces --- api/internal/managers/app/install/install.go | 22 +-- .../app/install/namespaces_reconciler.go | 130 +++++---------- .../app/install/namespaces_reconciler_test.go | 156 +++--------------- api/internal/utils/k8s.go | 11 ++ 4 files changed, 87 insertions(+), 232 deletions(-) create mode 100644 api/internal/utils/k8s.go diff --git a/api/internal/managers/app/install/install.go b/api/internal/managers/app/install/install.go index cb63108583..854bed801d 100644 --- a/api/internal/managers/app/install/install.go +++ b/api/internal/managers/app/install/install.go @@ -6,6 +6,7 @@ import ( "os" "runtime/debug" + "github.com/replicatedhq/embedded-cluster/api/internal/utils" "github.com/replicatedhq/embedded-cluster/api/types" "github.com/replicatedhq/embedded-cluster/pkg/helm" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" @@ -24,11 +25,18 @@ func (m *appInstallManager) Install(ctx context.Context, installableCharts []typ } // Start the namespace reconciler to ensure image pull secrets and other required resources in app namespaces - nsReconciler, err := runNamespaceReconciler(ctx, m.kcli, m.mcli, registrySettings, hostCABundlePath, m.logger) + nsReconciler, err := newNamespaceReconciler( + ctx, m.kcli, m.mcli, registrySettings, hostCABundlePath, + m.releaseData.ChannelRelease.AppSlug, m.releaseData.ChannelRelease.VersionLabel, + m.logger, + ) if err != nil { - return fmt.Errorf("start namespace reconciler: %w", err) + return fmt.Errorf("create namespace reconciler: %w", err) + } + + if err := nsReconciler.reconcile(ctx); err != nil { + return fmt.Errorf("reconcile namespaces: %w", err) } - defer nsReconciler.Stop() kotsadmNamespace, err := runtimeconfig.KotsadmNamespace(ctx, m.kcli) if err != nil { @@ -84,13 +92,7 @@ func (m *appInstallManager) createConfigValuesSecret(ctx context.Context, config ObjectMeta: metav1.ObjectMeta{ Name: secretName, Namespace: namespace, - Labels: map[string]string{ - "app.kubernetes.io/name": license.Spec.AppSlug, - "app.kubernetes.io/version": m.releaseData.ChannelRelease.VersionLabel, - "app.kubernetes.io/component": "config", - "app.kubernetes.io/part-of": "embedded-cluster", - "app.kubernetes.io/managed-by": "embedded-cluster-installer", - }, + Labels: utils.GetK8sObjectMetaLabels(license.Spec.AppSlug, m.releaseData.ChannelRelease.VersionLabel, "config"), }, Type: corev1.SecretTypeOpaque, Data: map[string][]byte{ diff --git a/api/internal/managers/app/install/namespaces_reconciler.go b/api/internal/managers/app/install/namespaces_reconciler.go index 74894f8cf5..a9965fe940 100644 --- a/api/internal/managers/app/install/namespaces_reconciler.go +++ b/api/internal/managers/app/install/namespaces_reconciler.go @@ -4,12 +4,10 @@ import ( "context" "encoding/base64" "fmt" - "slices" - "time" + "github.com/replicatedhq/embedded-cluster/api/internal/utils" "github.com/replicatedhq/embedded-cluster/api/types" "github.com/replicatedhq/embedded-cluster/pkg/addons/adminconsole" - addonstypes "github.com/replicatedhq/embedded-cluster/pkg/addons/types" "github.com/replicatedhq/embedded-cluster/pkg/release" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" "github.com/sirupsen/logrus" @@ -20,38 +18,32 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - reconcileInterval = 5 * time.Second -) - -// NamespaceReconciler handles ensuring image pull secrets and CA configmaps in app namespaces. -// It reads additionalNamespaces from the Application CR, ensures secrets and configmaps exist -// in those namespaces plus the kotsadm namespace, and polls for new namespace -// creation to deploy resources to them. -type NamespaceReconciler struct { +// namespaceReconciler handles ensuring image pull secrets and CA configmaps in app namespaces. +// It reads additionalNamespaces from the Application CR and ensures secrets and configmaps exist +// in those namespaces plus the kotsadm namespace. +type namespaceReconciler struct { kcli client.Client mcli metadata.Interface registrySettings *types.RegistrySettings hostCABundlePath string + appSlug string + versionLabel string logger logrus.FieldLogger - watchedNamespaces []string - cancel context.CancelFunc + namespaces []string } -// runNamespaceReconciler creates and starts a reconciler that: -// 1. Reads additionalNamespaces from release.GetApplication() -// 2. Immediately ensures image pull secrets and other resources in all watched namespaces -// 3. Starts background polling to reconcile namespaces periodically -// Returns a cancellable namespace reconciler instance. -func runNamespaceReconciler( +// newNamespaceReconciler creates a new namespace reconciler +func newNamespaceReconciler( ctx context.Context, kcli client.Client, mcli metadata.Interface, registrySettings *types.RegistrySettings, hostCABundlePath string, + appSlug string, + versionLabel string, logger logrus.FieldLogger, -) (*NamespaceReconciler, error) { +) (*namespaceReconciler, error) { // Get kotsadm namespace kotsadmNamespace, err := runtimeconfig.KotsadmNamespace(ctx, kcli) if err != nil { @@ -61,83 +53,42 @@ func runNamespaceReconciler( // Get watched namespaces from Application CR watchedNamespaces := []string{kotsadmNamespace} if app := release.GetApplication(); app != nil { - watchedNamespaces = append(watchedNamespaces, app.Spec.AdditionalNamespaces...) + for _, ns := range app.Spec.AdditionalNamespaces { + // NOTE: we no longer support watching all namespaces ("*") + if ns == "*" { + logger.Warn("watching all namespaces is not supported (\"*\")") + } else { + watchedNamespaces = append(watchedNamespaces, ns) + } + } } - ctx, cancel := context.WithCancel(ctx) - - r := &NamespaceReconciler{ - kcli: kcli, - mcli: mcli, - registrySettings: registrySettings, - hostCABundlePath: hostCABundlePath, - logger: logger, - watchedNamespaces: watchedNamespaces, - cancel: cancel, + r := &namespaceReconciler{ + kcli: kcli, + mcli: mcli, + registrySettings: registrySettings, + hostCABundlePath: hostCABundlePath, + appSlug: appSlug, + versionLabel: versionLabel, + logger: logger, + namespaces: watchedNamespaces, } - // Immediately reconcile all namespaces - r.reconcile(ctx) - - // Start background polling - go r.run(ctx) - return r, nil } -// Stop stops the background reconciler -func (r *NamespaceReconciler) Stop() { - if r.cancel != nil { - r.cancel() - } -} - -// run polls periodically to reconcile namespaces -func (r *NamespaceReconciler) run(ctx context.Context) { - ticker := time.NewTicker(reconcileInterval) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - r.reconcile(ctx) - } - } -} - // reconcile ensures all watched namespaces have the required resources -func (r *NamespaceReconciler) reconcile(ctx context.Context) { - namespaces := r.watchedNamespaces - - // If watching all namespaces, list them - if r.watchesAllNamespaces() { - nsList := &corev1.NamespaceList{} - if err := r.kcli.List(ctx, nsList); err != nil { - r.logger.WithError(err).Warn("failed to list namespaces") - return - } - namespaces = make([]string, 0, len(nsList.Items)) - for _, ns := range nsList.Items { - namespaces = append(namespaces, ns.Name) - } - } - - for _, ns := range namespaces { +func (r *namespaceReconciler) reconcile(ctx context.Context) error { + for _, ns := range r.namespaces { if err := r.reconcileNamespace(ctx, ns); err != nil { - r.logger.WithError(err).Warnf("failed to reconcile namespace %s", ns) + return fmt.Errorf("reconcile namespace %s: %w", ns, err) } } -} - -// watchesAllNamespaces returns true if "*" is in the watched namespaces list -func (r *NamespaceReconciler) watchesAllNamespaces() bool { - return slices.Contains(r.watchedNamespaces, "*") + return nil } // reconcileNamespace creates namespace if needed and ensures required resources exist -func (r *NamespaceReconciler) reconcileNamespace(ctx context.Context, namespace string) error { +func (r *namespaceReconciler) reconcileNamespace(ctx context.Context, namespace string) error { // Create namespace if it doesn't exist ns := &corev1.Namespace{} err := r.kcli.Get(ctx, client.ObjectKey{Name: namespace}, ns) @@ -165,7 +116,7 @@ func (r *NamespaceReconciler) reconcileNamespace(ctx context.Context, namespace } // ensureImagePullSecret creates or updates the image pull secret in a namespace -func (r *NamespaceReconciler) ensureImagePullSecret(ctx context.Context, namespace string) error { +func (r *namespaceReconciler) ensureImagePullSecret(ctx context.Context, namespace string) error { // Skip if no registry settings if r.registrySettings == nil || r.registrySettings.ImagePullSecretName == "" || r.registrySettings.ImagePullSecretValue == "" { return nil @@ -185,6 +136,7 @@ func (r *NamespaceReconciler) ensureImagePullSecret(ctx context.Context, namespa ObjectMeta: metav1.ObjectMeta{ Name: r.registrySettings.ImagePullSecretName, Namespace: namespace, + Labels: utils.GetK8sObjectMetaLabels(r.appSlug, r.versionLabel, "registry"), }, Type: corev1.SecretTypeDockerConfigJson, Data: map[string][]byte{ @@ -214,15 +166,11 @@ func (r *NamespaceReconciler) ensureImagePullSecret(ctx context.Context, namespa } // ensureCAConfigmap ensures the CA configmap exists in the namespace -func (r *NamespaceReconciler) ensureCAConfigmap(ctx context.Context, namespace string) error { +func (r *namespaceReconciler) ensureCAConfigmap(ctx context.Context, namespace string) error { // Skip if no CA bundle path if r.hostCABundlePath == "" { return nil } - logFn := func(format string, args ...interface{}) { - r.logger.Infof(format, args...) - } - - return adminconsole.EnsureCAConfigmap(ctx, addonstypes.LogFunc(logFn), r.kcli, r.mcli, namespace, r.hostCABundlePath) + return adminconsole.EnsureCAConfigmap(ctx, r.logger.Infof, r.kcli, r.mcli, namespace, r.hostCABundlePath) } diff --git a/api/internal/managers/app/install/namespaces_reconciler_test.go b/api/internal/managers/app/install/namespaces_reconciler_test.go index 0ec08e3a19..3954f3d965 100644 --- a/api/internal/managers/app/install/namespaces_reconciler_test.go +++ b/api/internal/managers/app/install/namespaces_reconciler_test.go @@ -4,7 +4,6 @@ import ( "encoding/base64" "os" "testing" - "time" "github.com/replicatedhq/embedded-cluster/api/pkg/logger" "github.com/replicatedhq/embedded-cluster/api/types" @@ -19,10 +18,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -func TestRunNamespaceReconciler(t *testing.T) { +func Test_namespaceReconciler_reconcile(t *testing.T) { dockerConfigJSON := `{"auths":{"registry.example.com":{"auth":"dXNlcjpwYXNz"}}}` appSlug := "test-app" + versionLabel := "1.0.0" tests := []struct { name string @@ -33,7 +33,7 @@ func TestRunNamespaceReconciler(t *testing.T) { existingSecrets []corev1.Secret existingConfigMaps []corev1.ConfigMap - wantWatchedNs []string + wantNamespaces []string wantCreatedNs []string wantSecretInNs []string wantNoSecretInNs []string @@ -49,7 +49,7 @@ func TestRunNamespaceReconciler(t *testing.T) { ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), }, existingNamespaces: []string{appSlug}, - wantWatchedNs: []string{appSlug}, + wantNamespaces: []string{appSlug}, wantCreatedNs: []string{}, wantSecretInNs: []string{appSlug}, wantNoCAConfigmapInNs: []string{appSlug}, @@ -67,7 +67,7 @@ spec: ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), }, existingNamespaces: []string{appSlug}, - wantWatchedNs: []string{appSlug}, + wantNamespaces: []string{appSlug}, wantCreatedNs: []string{}, wantSecretInNs: []string{appSlug}, wantNoCAConfigmapInNs: []string{appSlug}, @@ -88,13 +88,13 @@ spec: ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), }, existingNamespaces: []string{appSlug}, - wantWatchedNs: []string{appSlug, "app-ns-1", "app-ns-2"}, + wantNamespaces: []string{appSlug, "app-ns-1", "app-ns-2"}, wantCreatedNs: []string{"app-ns-1", "app-ns-2"}, wantSecretInNs: []string{appSlug, "app-ns-1", "app-ns-2"}, wantNoCAConfigmapInNs: []string{appSlug, "app-ns-1", "app-ns-2"}, }, { - name: "application with wildcard namespace", + name: "application with wildcard namespace - now skipped with warning", applicationYAML: `apiVersion: kots.io/v1beta1 kind: Application metadata: @@ -108,9 +108,10 @@ spec: ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), }, existingNamespaces: []string{appSlug, "existing-ns-1", "existing-ns-2"}, - wantWatchedNs: []string{appSlug, "*"}, + wantNamespaces: []string{appSlug}, // "*" is now skipped wantCreatedNs: []string{}, - wantSecretInNs: []string{appSlug, "existing-ns-1", "existing-ns-2"}, + wantSecretInNs: []string{appSlug}, // Only appSlug gets the secret + wantNoSecretInNs: []string{"existing-ns-1", "existing-ns-2"}, wantNoCAConfigmapInNs: []string{appSlug, "existing-ns-1", "existing-ns-2"}, }, { @@ -125,7 +126,7 @@ spec: - app-ns`, registrySettings: nil, existingNamespaces: []string{appSlug}, - wantWatchedNs: []string{appSlug, "app-ns"}, + wantNamespaces: []string{appSlug, "app-ns"}, wantCreatedNs: []string{"app-ns"}, wantSecretInNs: []string{}, wantNoSecretInNs: []string{appSlug, "app-ns"}, @@ -147,7 +148,7 @@ spec: }, withCABundle: true, existingNamespaces: []string{appSlug}, - wantWatchedNs: []string{appSlug, "app-ns"}, + wantNamespaces: []string{appSlug, "app-ns"}, wantCreatedNs: []string{"app-ns"}, wantSecretInNs: []string{appSlug, "app-ns"}, wantCAConfigmapInNs: []string{appSlug, "app-ns"}, @@ -177,7 +178,7 @@ spec: }, }, }, - wantWatchedNs: []string{appSlug}, + wantNamespaces: []string{appSlug}, wantCreatedNs: []string{}, wantSecretInNs: []string{appSlug}, }, @@ -209,7 +210,7 @@ spec: }, }, }, - wantWatchedNs: []string{appSlug}, + wantNamespaces: []string{appSlug}, wantCreatedNs: []string{}, wantSecretInNs: []string{appSlug}, wantCAConfigmapInNs: []string{appSlug}, @@ -261,26 +262,30 @@ spec: hostCABundlePath = tmpFile.Name() } - // Run the reconciler - reconciler, err := runNamespaceReconciler( + // Create the reconciler + reconciler, err := newNamespaceReconciler( t.Context(), fakeKcli, fakeMcli, tt.registrySettings, hostCABundlePath, + appSlug, + versionLabel, logger.NewDiscardLogger(), ) + require.NoError(t, err) + require.NotNil(t, reconciler) + + // Verify namespaces to be reconciled + assert.Equal(t, tt.wantNamespaces, reconciler.namespaces) + // Run the reconciler + err = reconciler.reconcile(t.Context()) if tt.wantErr { require.Error(t, err) return } require.NoError(t, err) - require.NotNil(t, reconciler) - defer reconciler.Stop() - - // Verify watched namespaces - assert.Equal(t, tt.wantWatchedNs, reconciler.watchedNamespaces) // Verify namespaces were created for _, nsName := range tt.wantCreatedNs { @@ -334,114 +339,3 @@ spec: }) } } - -func TestRunNamespaceReconciler_DynamicNamespace(t *testing.T) { - t.Setenv("ENABLE_V3", "1") - - dockerConfigJSON := `{"auths":{"registry.example.com":{"auth":"dXNlcjpwYXNz"}}}` - appSlug := "test-app" - - // Set up release data with wildcard namespace - releaseData := map[string][]byte{ - "channelrelease.yaml": []byte("# channel release object\nappSlug: " + appSlug), - "application.yaml": []byte(`apiVersion: kots.io/v1beta1 -kind: Application -metadata: - name: test-app -spec: - title: Test App - additionalNamespaces: - - "*"`), - } - require.NoError(t, release.SetReleaseDataForTests(releaseData)) - - // Start with only the app namespace - appNs := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: appSlug}, - } - fakeKcli := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(appNs).Build() - fakeMcli := metadatafake.NewSimpleMetadataClient(metadatafake.NewTestScheme()) - - registrySettings := &types.RegistrySettings{ - ImagePullSecretName: "test-secret", - ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)), - } - - // Run the reconciler - reconciler, err := runNamespaceReconciler( - t.Context(), - fakeKcli, - fakeMcli, - registrySettings, - "", - logger.NewDiscardLogger(), - ) - require.NoError(t, err) - defer reconciler.Stop() - - // Verify initial state - secret exists in app namespace - secret := &corev1.Secret{} - err = fakeKcli.Get(t.Context(), client.ObjectKey{ - Namespace: appSlug, - Name: "test-secret", - }, secret) - require.NoError(t, err, "secret should exist in app namespace") - - // Create a new namespace dynamically (simulating external namespace creation) - newNs := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: "dynamic-ns"}, - } - require.NoError(t, fakeKcli.Create(t.Context(), newNs)) - - // Wait for the background reconciler to create the secret in the new namespace - assert.Eventually(t, func() bool { - err := fakeKcli.Get(t.Context(), client.ObjectKey{ - Namespace: "dynamic-ns", - Name: "test-secret", - }, secret) - return err == nil - }, 10*time.Second, 100*time.Millisecond, "secret should be created in dynamic namespace by background reconciler") - - assert.Equal(t, corev1.SecretTypeDockerConfigJson, secret.Type) - assert.Equal(t, dockerConfigJSON, string(secret.Data[".dockerconfigjson"])) -} - -func TestNamespaceReconciler_watchesAllNamespaces(t *testing.T) { - tests := []struct { - name string - watchedNamespaces []string - want bool - }{ - { - name: "returns true when * is in list", - watchedNamespaces: []string{"kotsadm", "*"}, - want: true, - }, - { - name: "returns true when only * is in list", - watchedNamespaces: []string{"*"}, - want: true, - }, - { - name: "returns false when * is not in list", - watchedNamespaces: []string{"kotsadm", "app-ns"}, - want: false, - }, - { - name: "returns false for empty list", - watchedNamespaces: []string{}, - want: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := &NamespaceReconciler{ - watchedNamespaces: tt.watchedNamespaces, - } - - got := r.watchesAllNamespaces() - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/api/internal/utils/k8s.go b/api/internal/utils/k8s.go new file mode 100644 index 0000000000..fe774305c5 --- /dev/null +++ b/api/internal/utils/k8s.go @@ -0,0 +1,11 @@ +package utils + +func GetK8sObjectMetaLabels(appSlug string, versionLabel string, component string) map[string]string { + return map[string]string{ + "app.kubernetes.io/name": appSlug, + "app.kubernetes.io/version": versionLabel, + "app.kubernetes.io/component": component, + "app.kubernetes.io/part-of": "embedded-cluster", + "app.kubernetes.io/managed-by": "embedded-cluster-installer", + } +} From ba37be7d8abf4fc83dfe0e76c47d2a7530c45775 Mon Sep 17 00:00:00 2001 From: Ethan Mosbaugh Date: Thu, 11 Dec 2025 11:46:41 -0800 Subject: [PATCH 09/11] add test CalculateRegistrySettings --- .../managers/linux/installation/config.go | 24 +- .../linux/installation/config_test.go | 291 ++++++++++++++++-- 2 files changed, 271 insertions(+), 44 deletions(-) diff --git a/api/internal/managers/linux/installation/config.go b/api/internal/managers/linux/installation/config.go index 6b53121f09..f9991f9499 100644 --- a/api/internal/managers/linux/installation/config.go +++ b/api/internal/managers/linux/installation/config.go @@ -242,6 +242,12 @@ func (m *installationManager) CalculateRegistrySettings(ctx context.Context, rc return registrySettings, nil } + // Get app slug for secret name + if m.releaseData == nil || m.releaseData.ChannelRelease == nil || m.releaseData.ChannelRelease.AppSlug == "" { + return nil, fmt.Errorf("release data with app slug is required for registry settings") + } + appSlug := m.releaseData.ChannelRelease.AppSlug + // Use runtime config as the authoritative source for service CIDR serviceCIDR := rc.ServiceCIDR() @@ -253,12 +259,6 @@ func (m *installationManager) CalculateRegistrySettings(ctx context.Context, rc // Construct registry host with port registryHost := fmt.Sprintf("%s:5000", registryIP) - // Get app slug for secret name - if m.releaseData == nil || m.releaseData.ChannelRelease == nil || m.releaseData.ChannelRelease.AppSlug == "" { - return nil, fmt.Errorf("release data with app slug is required for registry settings") - } - appSlug := m.releaseData.ChannelRelease.AppSlug - // Construct full registry address with namespace appNamespace := appSlug // registry namespace is the same as the app slug in linux target registryAddress := fmt.Sprintf("%s/%s", registryHost, appNamespace) @@ -296,6 +296,12 @@ func (m *installationManager) GetRegistrySettings(ctx context.Context, rc runtim return registrySettings, nil } + // Get app slug for secret name + if m.releaseData == nil || m.releaseData.ChannelRelease == nil || m.releaseData.ChannelRelease.AppSlug == "" { + return nil, fmt.Errorf("release data with app slug is required for registry settings") + } + appSlug := m.releaseData.ChannelRelease.AppSlug + if m.kcli == nil { kcli, err := clients.NewKubeClient(clients.KubeClientOptions{KubeConfigPath: rc.PathToKubeConfig()}) if err != nil { @@ -357,12 +363,6 @@ func (m *installationManager) GetRegistrySettings(ctx context.Context, rc runtim return nil, fmt.Errorf("embedded-cluster username not found in registry-creds secret") } - // Get app namespace from release data - if m.releaseData == nil || m.releaseData.ChannelRelease == nil || m.releaseData.ChannelRelease.AppSlug == "" { - return nil, fmt.Errorf("release data with app slug is required for registry settings") - } - appSlug := m.releaseData.ChannelRelease.AppSlug - // Construct full registry address with namespace appNamespace := appSlug // registry namespace is the same as the app slug in linux target registryAddress := fmt.Sprintf("%s/%s", registryHost, appNamespace) diff --git a/api/internal/managers/linux/installation/config_test.go b/api/internal/managers/linux/installation/config_test.go index 57d1836506..fe86a3db9d 100644 --- a/api/internal/managers/linux/installation/config_test.go +++ b/api/internal/managers/linux/installation/config_test.go @@ -17,6 +17,10 @@ import ( kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" kyaml "sigs.k8s.io/yaml" ) @@ -609,44 +613,45 @@ func TestConfigureHost(t *testing.T) { } } -func TestCalculateRegistrySettings(t *testing.T) { - // Helper to create a test license - createTestLicense := func(licenseID, appSlug string) []byte { - license := kotsv1beta1.License{ - Spec: kotsv1beta1.LicenseSpec{ - LicenseID: licenseID, - AppSlug: appSlug, - }, - } - licenseBytes, _ := kyaml.Marshal(license) - return licenseBytes +// Helper to create a test license +func createTestLicense(licenseID, appSlug string) []byte { + license := kotsv1beta1.License{ + Spec: kotsv1beta1.LicenseSpec{ + LicenseID: licenseID, + AppSlug: appSlug, + }, } + licenseBytes, _ := kyaml.Marshal(license) + return licenseBytes +} - // Helper to create test release data - createTestReleaseData := func(appSlug string, domains *ecv1beta1.Domains) *release.ReleaseData { - releaseData := &release.ReleaseData{ - ChannelRelease: &release.ChannelRelease{ - AppSlug: appSlug, +// Helper to create test release data +func createTestReleaseData(appSlug string, domains *ecv1beta1.Domains) *release.ReleaseData { + releaseData := &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + AppSlug: appSlug, + }, + } + if domains != nil { + releaseData.EmbeddedClusterConfig = &ecv1beta1.Config{ + Spec: ecv1beta1.ConfigSpec{ + Domains: *domains, }, } - if domains != nil { - releaseData.EmbeddedClusterConfig = &ecv1beta1.Config{ - Spec: ecv1beta1.ConfigSpec{ - Domains: *domains, - }, - } - } - return releaseData } + return releaseData +} - // Helper to create runtime config - createTestRuntimeConfig := func() runtimeconfig.RuntimeConfig { - return runtimeconfig.New(&ecv1beta1.RuntimeConfigSpec{ - Network: ecv1beta1.NetworkSpec{ - ServiceCIDR: "10.96.0.0/12", - }, - }) - } +// Helper to create runtime config +func createTestRuntimeConfig() runtimeconfig.RuntimeConfig { + return runtimeconfig.New(&ecv1beta1.RuntimeConfigSpec{ + Network: ecv1beta1.NetworkSpec{ + ServiceCIDR: "10.96.0.0/12", + }, + }) +} + +func TestCalculateRegistrySettings(t *testing.T) { tests := []struct { name string @@ -782,3 +787,225 @@ func TestCalculateRegistrySettings(t *testing.T) { }) } } + +func TestGetRegistrySettings(t *testing.T) { + tests := []struct { + name string + license []byte + releaseData *release.ReleaseData + airgapBundle string + setupCluster func() client.Client + expectedResult *types.RegistrySettings + expectedError string + skipEnableV3Unset bool + }{ + { + name: "online mode with default domains", + license: createTestLicense("test-license-123", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "", // Online mode + expectedResult: &types.RegistrySettings{ + HasLocalRegistry: false, + ImagePullSecretName: "test-app-registry", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(`{"auths":{"proxy.replicated.com":{"username": "LICENSE_ID", "password": "test-license-123"},"registry.replicated.com":{"username": "LICENSE_ID", "password": "test-license-123"}}}`)), + }, + }, + { + name: "online mode with custom domains", + license: createTestLicense("custom-license-456", "custom-app"), + releaseData: createTestReleaseData("custom-app", &ecv1beta1.Domains{ + ProxyRegistryDomain: "custom-proxy.example.com", + ReplicatedRegistryDomain: "custom-registry.example.com", + }), + airgapBundle: "", // Online mode + expectedResult: &types.RegistrySettings{ + HasLocalRegistry: false, + ImagePullSecretName: "custom-app-registry", + ImagePullSecretValue: base64.StdEncoding.EncodeToString([]byte(`{"auths":{"custom-proxy.example.com":{"username": "LICENSE_ID", "password": "custom-license-456"},"custom-registry.example.com":{"username": "LICENSE_ID", "password": "custom-license-456"}}}`)), + }, + }, + { + name: "airgap mode with valid registry-creds secret", + license: createTestLicense("test-license", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "test-bundle.tar", + setupCluster: func() client.Client { + // Create a fake kubernetes client with the registry-creds secret + authString := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("embedded-cluster:%s", registry.GetRegistryPassword()))) + dockerConfigJSON := fmt.Sprintf(`{"auths":{"10.96.0.11:5000":{"username": "embedded-cluster", "password": "%s", "auth": "%s"}}}`, + registry.GetRegistryPassword(), authString) + + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "registry-creds", + Namespace: "kotsadm", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + ".dockerconfigjson": []byte(dockerConfigJSON), + }, + } + + return fake.NewClientBuilder().WithObjects(secret).Build() + }, + expectedResult: &types.RegistrySettings{ + HasLocalRegistry: true, + LocalRegistryHost: "10.96.0.11:5000", + LocalRegistryAddress: "10.96.0.11:5000/test-app", + LocalRegistryNamespace: "test-app", + LocalRegistryUsername: "embedded-cluster", + LocalRegistryPassword: registry.GetRegistryPassword(), + ImagePullSecretName: "test-app-registry", + ImagePullSecretValue: func() string { + authString := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("embedded-cluster:%s", registry.GetRegistryPassword()))) + dockerConfigJSON := fmt.Sprintf(`{"auths":{"10.96.0.11:5000":{"username": "embedded-cluster", "password": "%s", "auth": "%s"}}}`, + registry.GetRegistryPassword(), authString) + return base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON)) + }(), + }, + }, + { + name: "airgap mode with missing registry-creds secret", + license: createTestLicense("test-license", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "test-bundle.tar", + setupCluster: func() client.Client { + return fake.NewClientBuilder().Build() + }, + expectedError: "get registry-creds secret:", + }, + { + name: "airgap mode with invalid secret type", + license: createTestLicense("test-license", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "test-bundle.tar", + setupCluster: func() client.Client { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "registry-creds", + Namespace: "kotsadm", + }, + Type: corev1.SecretTypeOpaque, // Wrong type + Data: map[string][]byte{ + ".dockerconfigjson": []byte(`{}`), + }, + } + return fake.NewClientBuilder().WithObjects(secret).Build() + }, + expectedError: "registry-creds secret is not of type kubernetes.io/dockerconfigjson", + }, + { + name: "airgap mode with missing dockerconfigjson", + license: createTestLicense("test-license", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "test-bundle.tar", + setupCluster: func() client.Client { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "registry-creds", + Namespace: "kotsadm", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{}, // Missing .dockerconfigjson + } + return fake.NewClientBuilder().WithObjects(secret).Build() + }, + expectedError: "registry-creds secret missing .dockerconfigjson data", + }, + { + name: "airgap mode with invalid json in dockerconfigjson", + license: createTestLicense("test-license", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "test-bundle.tar", + setupCluster: func() client.Client { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "registry-creds", + Namespace: "kotsadm", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + ".dockerconfigjson": []byte("invalid json"), + }, + } + return fake.NewClientBuilder().WithObjects(secret).Build() + }, + expectedError: "parse dockerconfigjson:", + }, + { + name: "airgap mode with missing embedded-cluster username", + license: createTestLicense("test-license", "test-app"), + releaseData: createTestReleaseData("test-app", nil), + airgapBundle: "test-bundle.tar", + setupCluster: func() client.Client { + dockerConfigJSON := `{"auths":{"registry.example.com":{"username": "other-user", "password": "other-pass"}}}` + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "registry-creds", + Namespace: "kotsadm", + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + ".dockerconfigjson": []byte(dockerConfigJSON), + }, + } + return fake.NewClientBuilder().WithObjects(secret).Build() + }, + expectedError: "embedded-cluster username not found in registry-creds secret", + }, + { + name: "airgap mode missing release data", + license: createTestLicense("test-license", "test-app"), + releaseData: nil, + airgapBundle: "test-bundle.tar", + // No need to setup cluster - validation happens before cluster access + expectedError: "release data with app slug is required for registry settings", + }, + { + name: "airgap mode missing app slug", + license: createTestLicense("test-license", "test-app"), + releaseData: &release.ReleaseData{ + ChannelRelease: &release.ChannelRelease{ + AppSlug: "", // Empty app slug + }, + }, + airgapBundle: "test-bundle.tar", + // No need to setup cluster - validation happens before cluster access + expectedError: "release data with app slug is required for registry settings", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Ensure ENABLE_V3 is not set so KotsadmNamespace returns "kotsadm" + if !tt.skipEnableV3Unset { + t.Setenv("ENABLE_V3", "") + } + + rc := createTestRuntimeConfig() + + var kcli client.Client + if tt.setupCluster != nil { + kcli = tt.setupCluster() + } + + manager := NewInstallationManager( + WithLicense(tt.license), + WithReleaseData(tt.releaseData), + WithAirgapBundle(tt.airgapBundle), + WithKubeClient(kcli), + ) + + result, err := manager.GetRegistrySettings(context.Background(), rc) + + if tt.expectedError != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedError) + assert.Nil(t, result) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedResult, result) + } + }) + } +} From 30bff0fd4efbcdf59349460463dc5e0544dc1b3c Mon Sep 17 00:00:00 2001 From: Ethan Mosbaugh Date: Thu, 11 Dec 2025 12:52:34 -0800 Subject: [PATCH 10/11] fix dryrun tests --- tests/dryrun/util.go | 8 +++----- tests/dryrun/v3_install_test.go | 9 ++++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/dryrun/util.go b/tests/dryrun/util.go index aef6d85f82..0eaa977838 100644 --- a/tests/dryrun/util.go +++ b/tests/dryrun/util.go @@ -26,7 +26,6 @@ import ( "github.com/replicatedhq/embedded-cluster/pkg/helm" "github.com/replicatedhq/embedded-cluster/pkg/release" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -329,7 +328,7 @@ func assertSecretNotExists(t *testing.T, kcli client.Client, name string, namesp } // assertConfigValuesSecret validates that a config values secret exists with the expected values -func assertConfigValuesSecret(t *testing.T, kcli client.Client, name string, namespace string, expectedValues map[string]kotsv1beta1.ConfigValue) { +func assertConfigValuesSecret(t *testing.T, kcli client.Client, name string, namespace string, expectedValues map[string]apitypes.AppConfigValue) { t.Helper() // Get the secret @@ -353,17 +352,16 @@ func assertConfigValuesSecret(t *testing.T, kcli client.Client, name string, nam require.NotEmpty(t, data, "config-values.yaml should not be empty") // Unmarshal config values - var configValues kotsv1beta1.ConfigValues + var configValues apitypes.AppConfigValues err = yaml.Unmarshal(data, &configValues) require.NoError(t, err, "should be able to unmarshal config values from secret") // Validate each expected value for key, expectedValue := range expectedValues { - actualValue, exists := configValues.Spec.Values[key] + actualValue, exists := configValues[key] require.True(t, exists, "config value %s should exist", key) assert.Equal(t, expectedValue.Value, actualValue.Value, "config value %s should match", key) - assert.Equal(t, expectedValue.ValuePlaintext, actualValue.ValuePlaintext, "config value plaintext %s should match", key) if expectedValue.Filename != "" { assert.Equal(t, expectedValue.Filename, actualValue.Filename, "config value filename %s should match", key) diff --git a/tests/dryrun/v3_install_test.go b/tests/dryrun/v3_install_test.go index 63e78594eb..9d1269b8e1 100644 --- a/tests/dryrun/v3_install_test.go +++ b/tests/dryrun/v3_install_test.go @@ -19,7 +19,6 @@ import ( "github.com/replicatedhq/embedded-cluster/pkg/kubeutils" "github.com/replicatedhq/embedded-cluster/pkg/release" "github.com/replicatedhq/embedded-cluster/pkg/runtimeconfig" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -199,10 +198,10 @@ func validateHappyPathOnline(t *testing.T, hcli *helm.MockClient) { assertSecretExists(t, kcli, "fake-app-slug-registry", adminConsoleNamespace) // Validate config values secret exists and contains correct values - assertConfigValuesSecret(t, kcli, "fake-app-slug-config-values", adminConsoleNamespace, map[string]kotsv1beta1.ConfigValue{ + assertConfigValuesSecret(t, kcli, "fake-app-slug-config-values", adminConsoleNamespace, map[string]apitypes.AppConfigValue{ "text_required": {Value: "text required value"}, "text_required_with_regex": {Value: "ethan@replicated.com"}, - "password_required": {ValuePlaintext: "password required value"}, + "password_required": {Value: "password required value"}, "file_required": { Value: "ZmlsZSByZXF1aXJlZCB2YWx1ZQo=", Filename: "file_required.txt", @@ -406,10 +405,10 @@ func validateHappyPathAirgap(t *testing.T, hcli *helm.MockClient) { require.True(t, found, "redis-app helm release should be installed") // Validate config values secret exists and contains correct values - assertConfigValuesSecret(t, kcli, "fake-app-slug-config-values", adminConsoleNamespace, map[string]kotsv1beta1.ConfigValue{ + assertConfigValuesSecret(t, kcli, "fake-app-slug-config-values", adminConsoleNamespace, map[string]apitypes.AppConfigValue{ "text_required": {Value: "text required value"}, "text_required_with_regex": {Value: "ethan@replicated.com"}, - "password_required": {ValuePlaintext: "password required value"}, + "password_required": {Value: "password required value"}, "file_required": { Value: "ZmlsZSByZXF1aXJlZCB2YWx1ZQo=", Filename: "file_required.txt", From bf8d921e28f7214c0a140fc5aa4bc8a48ffa6d48 Mon Sep 17 00:00:00 2001 From: Ethan Mosbaugh Date: Thu, 11 Dec 2025 12:53:41 -0800 Subject: [PATCH 11/11] cursor feedback --- api/internal/managers/app/install/install.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/internal/managers/app/install/install.go b/api/internal/managers/app/install/install.go index 854bed801d..b9f5d71f14 100644 --- a/api/internal/managers/app/install/install.go +++ b/api/internal/managers/app/install/install.go @@ -24,6 +24,10 @@ func (m *appInstallManager) Install(ctx context.Context, installableCharts []typ return fmt.Errorf("setup clients: %w", err) } + if m.releaseData == nil || m.releaseData.ChannelRelease == nil { + return fmt.Errorf("release data is required for app installation") + } + // Start the namespace reconciler to ensure image pull secrets and other required resources in app namespaces nsReconciler, err := newNamespaceReconciler( ctx, m.kcli, m.mcli, registrySettings, hostCABundlePath,