From 8c3cd3aeccda871a551dbf399d3730c702974c84 Mon Sep 17 00:00:00 2001 From: Salah Al Saleh Date: Thu, 25 Apr 2024 18:14:32 -0700 Subject: [PATCH] Remove helm managed mode (#4572) --- e2e/README.md | 11 - e2e/e2e_test.go | 16 +- e2e/inventory/inventory.go | 18 - e2e/inventory/test.go | 1 - e2e/playwright/playwright.config.ts | 13 +- pkg/api/handlers/types/types.go | 10 - pkg/apiserver/bootstrap.go | 13 +- pkg/apiserver/server.go | 96 ++- pkg/app/types/helm_app.go | 42 -- pkg/app/types/types.go | 8 - pkg/filestore/blob_store.go | 11 - pkg/handlers/app.go | 208 +------ pkg/handlers/config.go | 435 +++++-------- pkg/handlers/dashboard.go | 28 +- pkg/handlers/downstream.go | 48 +- pkg/handlers/handlers.go | 8 - pkg/handlers/handlers_test.go | 32 - pkg/handlers/helm.go | 197 ------ pkg/handlers/interface.go | 5 - pkg/handlers/license.go | 164 ++--- pkg/handlers/metadata.go | 4 +- pkg/handlers/mock/mock.go | 36 -- pkg/handlers/troubleshoot.go | 79 +-- pkg/handlers/update.go | 45 +- pkg/handlers/update_checker_spec.go | 82 +-- pkg/helm/cache.go | 321 ---------- pkg/helm/charts.go | 582 ------------------ pkg/helm/helm.go | 253 -------- pkg/helm/helm_test.go | 181 ------ pkg/helm/informers.go | 181 ------ pkg/helm/license.go | 46 -- pkg/helm/update_release.go | 26 +- pkg/helm/update_release_test.go | 2 +- pkg/helm/updates.go | 455 -------------- pkg/policy/getters.go | 30 +- pkg/policy/policies.go | 7 - pkg/redact/app.go | 2 +- pkg/render/helper/appfile.go | 14 +- pkg/replicatedapp/api.go | 10 - pkg/reporting/app.go | 117 +--- pkg/store/kotsstore/supportbundle_store.go | 196 ++---- pkg/supportbundle/helm.go | 69 --- pkg/supportbundle/spec.go | 89 +-- pkg/supportbundle/supportbundle.go | 71 +-- pkg/updatechecker/updatechecker.go | 153 +---- pkg/util/util.go | 4 - web/src/Root.tsx | 41 -- web/src/components/apps/AppDetailPage.tsx | 5 - web/src/components/apps/AppLicense.tsx | 59 +- web/src/components/apps/AppVersionHistory.tsx | 190 +----- web/src/components/hooks/index.ts | 11 - web/src/components/hooks/useDownloadValues.js | 122 ---- .../hooks/useDownloadValues.test.jsx | 174 ------ web/src/components/hooks/useIsHelmManaged.tsx | 50 -- web/src/components/hooks/useSaveConfig.js | 33 - .../components/hooks/useSaveConfig.test.jsx | 157 ----- .../modals/AutomaticUpdatesModal.tsx | 20 +- web/src/components/shared/EditConfigIcon.tsx | 5 +- web/src/components/shared/NavBar.tsx | 2 - web/src/components/shared/NavBarDropdown.jsx | 4 +- .../components/shared/SubNavBar/SubNavBar.jsx | 2 - .../shared/modals/HelmDeployModal.test.js | 3 - .../shared/modals/HelmDeployModal.tsx | 200 ------ .../components/tree/KotsApplicationTree.tsx | 23 +- .../troubleshoot/GenerateSupportBundle.jsx | 6 +- .../GenerateSupportBundleModal.tsx | 6 +- web/src/config-ui/subNavConfig.js | 8 +- .../AppConfig/components/AppConfig.tsx | 163 ++--- .../AppVersionHistoryRow.tsx | 59 +- .../AppVersionHistory/api/getVersions.tsx | 47 -- .../Auth/components/SecureAdminConsole.tsx | 4 - .../api/getSelectedAppClusterDashboard.ts | 9 +- .../Dashboard/components/Dashboard.tsx | 4 - .../components/DashboardGraphsCard.tsx | 4 - .../components/DashboardVersionCard.tsx | 134 +--- .../features/VersionDiff/ViewDiffButton.tsx | 5 +- .../stories/AutomaticUpdatesModal.stories.tsx | 1 - web/src/stories/HelmDeployModal.stories.tsx | 35 -- web/src/stories/ShowLogsModal.stories.tsx | 9 - 79 files changed, 551 insertions(+), 5463 deletions(-) delete mode 100644 pkg/app/types/helm_app.go delete mode 100644 pkg/handlers/helm.go delete mode 100644 pkg/helm/cache.go delete mode 100644 pkg/helm/charts.go delete mode 100644 pkg/helm/helm.go delete mode 100644 pkg/helm/helm_test.go delete mode 100644 pkg/helm/informers.go delete mode 100644 pkg/helm/license.go delete mode 100644 pkg/helm/updates.go delete mode 100644 pkg/supportbundle/helm.go delete mode 100644 web/src/components/hooks/index.ts delete mode 100644 web/src/components/hooks/useDownloadValues.js delete mode 100644 web/src/components/hooks/useDownloadValues.test.jsx delete mode 100644 web/src/components/hooks/useIsHelmManaged.tsx delete mode 100644 web/src/components/hooks/useSaveConfig.js delete mode 100644 web/src/components/hooks/useSaveConfig.test.jsx delete mode 100644 web/src/components/shared/modals/HelmDeployModal.test.js delete mode 100644 web/src/components/shared/modals/HelmDeployModal.tsx delete mode 100644 web/src/stories/HelmDeployModal.stories.tsx diff --git a/e2e/README.md b/e2e/README.md index 45290b878f..9a29ccff33 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -44,17 +44,6 @@ make e2e \ KOTSADM_IMAGE_TAG=24h ``` -To run a helm-managed mode test: - -*Note the admin console helm chart is maintained in a [separate repo](https://github.com/replicatedhq/kots-helm)* - -```bash -make e2e \ - FOCUS="Helm Managed" - KOTS_HELM_CHART_URL=oci://ttl.sh/$USER/admin-console - KOTS_HELM_CHART_VERSION=$VERSION -``` - To run using a specific testim branch: ```bash make e2e \ diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index b0017131cf..250c399661 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -3,7 +3,6 @@ package e2e import ( "context" "flag" - "fmt" "os" "testing" "time" @@ -178,18 +177,8 @@ var _ = Describe("E2E", func() { prometheus.Install(helmCLI, c.GetKubeconfig()) } - var adminConsolePort string - if test.IsHelmManaged { - GinkgoWriter.Println("Installing KOTS Helm chart") - session, err := helmCLI.Install(c.GetKubeconfig(), "-n", test.Namespace, "admin-console", kotsHelmChartURL, "--set", fmt.Sprintf("password=%s", inventory.HelmPassword), "--version", kotsHelmChartVersion, "--create-namespace", "--wait") - Expect(err).WithOffset(1).Should(Succeed(), "helm install") - Eventually(session).WithOffset(1).WithTimeout(time.Minute).Should(gexec.Exit(0), "helm install failed with non-zero exit code") - - adminConsolePort = kotsInstaller.AdminConsolePortForward(c.GetKubeconfig(), test, kotsadmForwardPort) - } else { - GinkgoWriter.Println("Installing KOTS") - adminConsolePort = kotsInstaller.Install(c.GetKubeconfig(), test, kotsadmForwardPort) - } + GinkgoWriter.Println("Installing KOTS") + adminConsolePort := kotsInstaller.Install(c.GetKubeconfig(), test, kotsadmForwardPort) GinkgoWriter.Println("Running E2E tests") @@ -223,7 +212,6 @@ var _ = Describe("E2E", func() { Entry(nil, inventory.NewNoRequiredConfig()), Entry(nil, inventory.NewVersionHistoryPagination()), Entry(nil, inventory.NewChangeLicense()), - Entry(nil, inventory.NewHelmManagedMode()), Entry(nil, inventory.NewMinKotsVersion()), Entry(nil, inventory.NewTargetKotsVersion()), Entry(nil, inventory.NewRangeKotsVersion()), diff --git a/e2e/inventory/inventory.go b/e2e/inventory/inventory.go index e1607ec401..7b2affdff8 100644 --- a/e2e/inventory/inventory.go +++ b/e2e/inventory/inventory.go @@ -102,17 +102,6 @@ func NewChangeLicense() Test { } } -func NewHelmManagedMode() Test { - return Test{ - Name: "Helm Managed", - TestimSuite: "helm-managed", - Namespace: "helm-managed", - UpstreamURI: "helm-managed/automated", - IsHelmManaged: true, - Setup: SetupHelmManagedMode, - } -} - func NewMultiAppBackupAndRestoreTest() Test { return Test{ Name: "multi-app-backup-and-restore", @@ -197,10 +186,3 @@ func SetupRegressionTest(kubectlCLI *kubectl.CLI) TestimParams { Eventually(session).WithOffset(1).WithTimeout(30*time.Minute).Should(gexec.Exit(0), "Create registry-creds secret failed with non-zero exit code") return nil } - -func SetupHelmManagedMode(kubectlCLI *kubectl.CLI) TestimParams { - return TestimParams{ - "kotsadmPassword": HelmPassword, - "kotsadmNamespace": "helm-managed", - } -} diff --git a/e2e/inventory/test.go b/e2e/inventory/test.go index fdcab9a498..1fe11735d0 100644 --- a/e2e/inventory/test.go +++ b/e2e/inventory/test.go @@ -18,6 +18,5 @@ type Test struct { NeedsSnapshots bool NeedsMonitoring bool NeedsRegistry bool - IsHelmManaged bool Setup func(kubectlCLI *kubectl.CLI) TestimParams } diff --git a/e2e/playwright/playwright.config.ts b/e2e/playwright/playwright.config.ts index fa279694a2..a5e2dbe95e 100644 --- a/e2e/playwright/playwright.config.ts +++ b/e2e/playwright/playwright.config.ts @@ -15,8 +15,6 @@ export default defineConfig({ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, - /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ @@ -26,8 +24,15 @@ export default defineConfig({ /* Base URL to use in actions like `await page.goto('/')`. */ baseURL: `http://localhost:${process.env.PORT || 8800}`, - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + /* + To include traces for failed tests, set this to 'retain-on-failure'. + This is not enabled by default because it's performance heavy. + See https://playwright.dev/docs/trace-viewer. + */ + trace: 'off', + + /* Screenshot on failure. */ + screenshot: 'only-on-failure', }, /* Configure projects for major browsers */ diff --git a/pkg/api/handlers/types/types.go b/pkg/api/handlers/types/types.go index 6df5a0a619..9572c3e350 100644 --- a/pkg/api/handlers/types/types.go +++ b/pkg/api/handlers/types/types.go @@ -13,10 +13,6 @@ type ListAppsResponse struct { Apps []ResponseApp `json:"apps"` } -type ListAppsHelmResponse struct { - Apps []HelmResponseApp `json:"apps"` -} - type AppStatusResponse struct { AppStatus *appstatetypes.AppStatus `json:"appstatus"` } @@ -58,12 +54,6 @@ type Credentials struct { Password string `json:"password"` } -type HelmResponseApp struct { - ResponseApp - ChartPath string `json:"chartPath,omitempty"` - Credentials Credentials `json:"credentials"` -} - type ResponseDownstream struct { Name string `json:"name"` Links []versiontypes.RealizedLink `json:"links"` diff --git a/pkg/apiserver/bootstrap.go b/pkg/apiserver/bootstrap.go index 90d0a746ac..29d5f7ff49 100644 --- a/pkg/apiserver/bootstrap.go +++ b/pkg/apiserver/bootstrap.go @@ -9,7 +9,6 @@ import ( "github.com/replicatedhq/kots/pkg/crypto" "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/store" - "github.com/replicatedhq/kots/pkg/util" ) type BootstrapParams struct { @@ -21,13 +20,11 @@ func bootstrap(params BootstrapParams) error { return errors.Wrap(err, "failed to init store") } - if !util.IsHelmManaged() { - if err := bootstrapClusterToken(params.AutoCreateClusterToken); err != nil { - return errors.Wrap(err, "failed to bootstrap cluster token") - } - if err := loadEncryptionKeys(); err != nil { - return errors.Wrap(err, "failed to load encryption keys") - } + if err := bootstrapClusterToken(params.AutoCreateClusterToken); err != nil { + return errors.Wrap(err, "failed to bootstrap cluster token") + } + if err := loadEncryptionKeys(); err != nil { + return errors.Wrap(err, "failed to load encryption keys") } return nil diff --git a/pkg/apiserver/server.go b/pkg/apiserver/server.go index e13962e0d2..9323aa32c2 100644 --- a/pkg/apiserver/server.go +++ b/pkg/apiserver/server.go @@ -15,12 +15,11 @@ import ( "github.com/replicatedhq/kots/pkg/binaries" "github.com/replicatedhq/kots/pkg/embeddedcluster" "github.com/replicatedhq/kots/pkg/handlers" - "github.com/replicatedhq/kots/pkg/helm" identitymigrate "github.com/replicatedhq/kots/pkg/identity/migrate" "github.com/replicatedhq/kots/pkg/informers" "github.com/replicatedhq/kots/pkg/k8sutil" "github.com/replicatedhq/kots/pkg/operator" - "github.com/replicatedhq/kots/pkg/operator/client" + operatorclient "github.com/replicatedhq/kots/pkg/operator/client" "github.com/replicatedhq/kots/pkg/persistence" "github.com/replicatedhq/kots/pkg/policy" "github.com/replicatedhq/kots/pkg/rbac" @@ -44,24 +43,20 @@ type APIServerParams struct { func Start(params *APIServerParams) { log.Printf("kotsadm version %s\n", params.Version) - if !util.IsHelmManaged() { - // set some persistence variables - persistence.InitDB(params.RqliteURI) + // set some persistence variables + persistence.InitDB(params.RqliteURI) - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - if err := store.GetStore().WaitForReady(ctx); err != nil { - log.Println("error waiting for ready") - panic(err) - } - cancel() + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + if err := store.GetStore().WaitForReady(ctx); err != nil { + log.Println("error waiting for ready") + panic(err) } + cancel() // check if we need to migrate from postgres before doing anything else - if !util.IsHelmManaged() { - if err := persistence.MigrateFromPostgresToRqlite(); err != nil { - log.Println("error migrating from postgres to rqlite") - panic(err) - } + if err := persistence.MigrateFromPostgresToRqlite(); err != nil { + log.Println("error migrating from postgres to rqlite") + panic(err) } if err := bootstrap(BootstrapParams{ @@ -71,11 +66,9 @@ func Start(params *APIServerParams) { panic(err) } - if !util.IsHelmManaged() { - store.GetStore().RunMigrations() - if err := identitymigrate.RunMigrations(context.TODO(), util.PodNamespace); err != nil { - log.Println("Failed to run identity migrations: ", err) - } + store.GetStore().RunMigrations() + if err := identitymigrate.RunMigrations(context.TODO(), util.PodNamespace); err != nil { + log.Println("Failed to run identity migrations: ", err) } if err := binaries.InitKubectl(); err != nil { @@ -88,28 +81,27 @@ func Start(params *APIServerParams) { panic(err) } - if !util.IsHelmManaged() { - client := &client.Client{ - TargetNamespace: util.AppNamespace(), - ExistingHookInformers: map[string]bool{}, - HookStopChans: []chan struct{}{}, - } - store := store.GetStore() - k8sClientset, err := k8sutil.GetClientset() - if err != nil { - log.Println("error getting k8s clientset") - panic(err) - } - op := operator.Init(client, store, params.AutocreateClusterToken, k8sClientset) - if err := op.Start(); err != nil { - log.Println("error starting the operator") - panic(err) - } - defer op.Shutdown() + kotsStore := store.GetStore() - if err := embeddedcluster.InitClusterState(context.TODO(), k8sClientset, store); err != nil { - log.Println("Failed to initialize cluster state:", err) - } + operatorClient := &operatorclient.Client{ + TargetNamespace: util.AppNamespace(), + ExistingHookInformers: map[string]bool{}, + HookStopChans: []chan struct{}{}, + } + k8sClientset, err := k8sutil.GetClientset() + if err != nil { + log.Println("error getting k8s clientset") + panic(err) + } + op := operator.Init(operatorClient, kotsStore, params.AutocreateClusterToken, k8sClientset) + if err := op.Start(); err != nil { + log.Println("error starting the operator") + panic(err) + } + defer op.Shutdown() + + if err := embeddedcluster.InitClusterState(context.TODO(), k8sClientset, kotsStore); err != nil { + log.Println("Failed to initialize cluster state:", err) } if params.SharedPassword != "" { @@ -136,13 +128,11 @@ func Start(params *APIServerParams) { log.Println("Failed to start informers:", err) } - if !util.IsHelmManaged() { - if err := updatechecker.Start(); err != nil { - log.Println("Failed to start update checker:", err) - } - if err := snapshotscheduler.Start(); err != nil { - log.Println("Failed to start snapshot scheduler:", err) - } + if err := updatechecker.Start(); err != nil { + log.Println("Failed to start update checker:", err) + } + if err := snapshotscheduler.Start(); err != nil { + log.Println("Failed to start snapshot scheduler:", err) } if err := session.StartSessionPurgeCronJob(); err != nil { @@ -159,12 +149,6 @@ func Start(params *APIServerParams) { } } - if util.IsHelmManaged() { - if err := helm.Init(context.TODO()); err != nil { - log.Println("Failed to initialize helm data: ", err) - } - } - r := mux.NewRouter() r.Use(handlers.CorsMiddleware) @@ -178,8 +162,6 @@ func Start(params *APIServerParams) { handler := &handlers.Handler{} - kotsStore := store.GetStore() - /********************************************************************** * Unauthenticated routes **********************************************************************/ diff --git a/pkg/app/types/helm_app.go b/pkg/app/types/helm_app.go deleted file mode 100644 index 9d4df17407..0000000000 --- a/pkg/app/types/helm_app.go +++ /dev/null @@ -1,42 +0,0 @@ -package types - -import ( - "time" - - appstatetypes "github.com/replicatedhq/kots/pkg/appstate/types" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - helmrelease "helm.sh/helm/v3/pkg/release" -) - -type HelmApp struct { - Release helmrelease.Release - Labels map[string]string - Version int64 // populated from labels - Namespace string - IsConfigurable bool - ChartPath string - CreationTimestamp time.Time - Status appstatetypes.AppStatus - // TODO: This is values the user is editing on the Config screen. This is a temporary solution while we figure out the UX. - TempConfigValues map[string]kotsv1beta1.ConfigValue -} - -func (a *HelmApp) GetID() string { - return a.Release.Name -} - -func (a *HelmApp) GetSlug() string { - return a.Release.Name -} - -func (a *HelmApp) GetCurrentSequence() int64 { - return a.Version -} - -func (a *HelmApp) GetIsAirgap() bool { - return false // no airgap support yet -} - -func (a *HelmApp) GetNamespace() string { - return a.Namespace -} diff --git a/pkg/app/types/types.go b/pkg/app/types/types.go index 6dcc9d71f6..4f5b902913 100644 --- a/pkg/app/types/types.go +++ b/pkg/app/types/types.go @@ -18,11 +18,3 @@ const ( AutoDeploySemverMajorMinorPatch AutoDeploy = "semver-major-minor-patch" AutoDeploySequence AutoDeploy = "sequence" ) - -type AppType interface { - GetID() string - GetSlug() string - GetCurrentSequence() int64 - GetIsAirgap() bool - GetNamespace() string -} diff --git a/pkg/filestore/blob_store.go b/pkg/filestore/blob_store.go index f724519c7d..ce8744ea83 100644 --- a/pkg/filestore/blob_store.go +++ b/pkg/filestore/blob_store.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/pkg/errors" - "github.com/replicatedhq/kots/pkg/util" ) var ( @@ -20,16 +19,6 @@ type BlobStore struct { } func (s *BlobStore) Init() error { - if util.IsHelmManaged() { - // Helm managed mode does not have any persisten storage. - dir, err := ioutil.TempDir("", "kotsadmdata-archives-") - if err != nil { - return errors.Wrapf(err, "failed to create ephemeral archives directory") - } - ArchivesDir = dir - return nil - } - err := os.MkdirAll(ArchivesDir, 0755) if err != nil { return errors.Wrapf(err, "failed to create archives directory") diff --git a/pkg/handlers/app.go b/pkg/handlers/app.go index 7e33e63190..522bd33766 100644 --- a/pkg/handlers/app.go +++ b/pkg/handlers/app.go @@ -17,7 +17,6 @@ import ( apptypes "github.com/replicatedhq/kots/pkg/app/types" "github.com/replicatedhq/kots/pkg/embeddedcluster" "github.com/replicatedhq/kots/pkg/gitops" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/k8sutil" "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/logger" @@ -42,11 +41,6 @@ func (h *Handler) GetPendingApp(w http.ResponseWriter, r *http.Request) { return } - if util.IsHelmManaged() { - w.WriteHeader(http.StatusNotFound) - return - } - papp, err := store.GetStore().GetPendingAirgapUploadApp() if err != nil { if store.GetStore().IsNotFound(err) { @@ -100,33 +94,6 @@ func (h *Handler) ListApps(w http.ResponseWriter, r *http.Request) { return } - responseApps := []types.ResponseApp{} - if util.IsHelmManaged() { - helmResponseApps := []types.HelmResponseApp{} - - for _, releaseName := range helm.GetCachedHelmApps() { - release := helm.GetHelmApp(releaseName) - if release == nil { - continue - } - - app, err := helm.ResponseAppFromHelmApp(release) - if err != nil { - logger.Error(errors.Wrap(err, "failed to convert release to app")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - helmResponseApps = append(helmResponseApps, *app) - } - - listAppsResponse := types.ListAppsHelmResponse{ - Apps: helmResponseApps, - } - - JSON(w, http.StatusOK, listAppsResponse) - return - } apps, err := store.GetStore().ListInstalledApps() if err != nil { logger.Error(err) @@ -134,6 +101,7 @@ func (h *Handler) ListApps(w http.ResponseWriter, r *http.Request) { return } + responseApps := []types.ResponseApp{} defaultRoles := rbac.DefaultRoles() // TODO (ethan): this should be set in the handler for _, a := range apps { @@ -188,31 +156,13 @@ func (h *Handler) GetAppStatus(w http.ResponseWriter, r *http.Request) { func (h *Handler) GetApp(w http.ResponseWriter, r *http.Request) { appSlug := mux.Vars(r)["appSlug"] - responseApp := new(types.ResponseApp) - if util.IsHelmManaged() { - release := helm.GetHelmApp(appSlug) - if release == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - app, err := helm.ResponseAppFromHelmApp(release) - if err != nil { - logger.Error(errors.Wrap(err, "failed to convert release to app")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - JSON(w, http.StatusOK, app) - return - } a, err := store.GetStore().GetAppFromSlug(appSlug) if err != nil { logger.Error(err) w.WriteHeader(http.StatusInternalServerError) return } - responseApp, err = responseAppFromApp(a) + responseApp, err := responseAppFromApp(a) if err != nil { logger.Error(err) w.WriteHeader(http.StatusInternalServerError) @@ -432,87 +382,37 @@ func (h *Handler) GetAppVersionHistory(w http.ResponseWriter, r *http.Request) { } appSlug := mux.Vars(r)["appSlug"] - history := new(downstreamtypes.DownstreamVersionHistory) - if util.IsHelmManaged() { - release := helm.GetHelmApp(appSlug) - if release == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - history.NumOfRemainingVersions = 0 - chartUpdates := helm.GetDownloadedUpdates(release.ChartPath) - - installedReleases, err := helm.ListChartVersions(appSlug, release.Namespace) - if err != nil { - err = errors.Wrapf(err, "failed to get installed releases of %s", appSlug) - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - installedVersions := []*downstreamtypes.DownstreamVersion{} - for _, installedRelease := range installedReleases { - installedVersions = append(installedVersions, helmReleaseToDownsreamVersion(&installedRelease)) - } - - // Parity with Helm history output, which lists revisions sorted by revision number in descending order. - downstreamtypes.SortDownstreamVersions(installedVersions, false) - - lastInstalledSequence := 0 - if len(installedVersions) > 0 { - lastInstalledSequence = int(installedVersions[0].ParentSequence) - } - - newVersions := make([]*downstreamtypes.DownstreamVersion, len(chartUpdates), len(chartUpdates)) - nextUpdateSequence := lastInstalledSequence + 1 - for i := len(chartUpdates) - 1; i >= 0; i-- { - newVersions[i] = helm.HelmUpdateToDownsreamVersion(chartUpdates[i], int64(nextUpdateSequence)) - nextUpdateSequence = nextUpdateSequence + 1 - } - - numSkippedVersions := len(newVersions) - 1 // looks like this is what getLatestDeployableDownstreamVersion does - if pinLatestDeployable && len(newVersions) > 0 { - // TODO: this should be UI logic. the response here should have no duplicates on the list - newVersions = append([]*downstreamtypes.DownstreamVersion{newVersions[0]}, newVersions...) - } - versions := append(newVersions, installedVersions...) - - history.VersionHistory = versions - history.TotalCount = len(versions) - history.NumOfSkippedVersions = numSkippedVersions - } else { - foundApp, err := store.GetStore().GetAppFromSlug(appSlug) - if err != nil { - err = errors.Wrap(err, "failed to get app from slug") - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } + foundApp, err := store.GetStore().GetAppFromSlug(appSlug) + if err != nil { + err = errors.Wrap(err, "failed to get app from slug") + logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return + } - downstreams, err := store.GetStore().ListDownstreamsForApp(foundApp.ID) - if err != nil { - err = errors.Wrap(err, "failed to list downstreams for app") - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } else if len(downstreams) == 0 { - err = errors.New("no downstreams for app") - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } + downstreams, err := store.GetStore().ListDownstreamsForApp(foundApp.ID) + if err != nil { + err = errors.Wrap(err, "failed to list downstreams for app") + logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return + } else if len(downstreams) == 0 { + err = errors.New("no downstreams for app") + logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return + } - clusterID := downstreams[0].ClusterID + clusterID := downstreams[0].ClusterID - history, err = store.GetStore().GetDownstreamVersionHistory(foundApp.ID, clusterID, currentPage, pageSize, pinLatest, pinLatestDeployable) - if err != nil { - err = errors.Wrap(err, "failed to get downstream versions") - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } + history, err := store.GetStore().GetDownstreamVersionHistory(foundApp.ID, clusterID, currentPage, pageSize, pinLatest, pinLatestDeployable) + if err != nil { + err = errors.Wrap(err, "failed to get downstream versions") + logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return } + response := GetAppVersionHistoryResponse{ DownstreamVersionHistory: *history, } @@ -714,39 +614,6 @@ func (h *Handler) GetLatestDeployableVersion(w http.ResponseWriter, r *http.Requ getLatestDeployableVersionResponse := GetLatestDeployableVersionResponse{} appSlug := mux.Vars(r)["appSlug"] - - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - availableUpdates := helm.GetCachedUpdates(helmApp.ChartPath) - if len(availableUpdates) == 0 { - JSON(w, http.StatusOK, getLatestDeployableVersionResponse) - return - } - - installedReleases, err := helm.ListChartVersions(appSlug, helmApp.Namespace) - if err != nil { - errMsg := "failed to get installed releases" - logger.Error(errors.Wrap(err, errMsg)) - getLatestDeployableVersionResponse.Error = errMsg - JSON(w, http.StatusInternalServerError, getLatestDeployableVersionResponse) - return - } - - getLatestDeployableVersionResponse.Error = "" - sequence := len(installedReleases) + len(availableUpdates) // helm revisions are 1-based - getLatestDeployableVersionResponse.LatestDeployableVersion = helm.HelmUpdateToDownsreamVersion(availableUpdates[0], int64(sequence)) - getLatestDeployableVersionResponse.NumOfSkippedVersions = 0 // TODO - getLatestDeployableVersionResponse.NumOfRemainingVersions = 0 // TODO - - JSON(w, http.StatusOK, getLatestDeployableVersionResponse) - return - } - a, err := store.GetStore().GetAppFromSlug(appSlug) if err != nil { errMsg := "failed to get app from slug" @@ -802,20 +669,3 @@ func (h *Handler) GetAutomatedInstallStatus(w http.ResponseWriter, r *http.Reque JSON(w, http.StatusOK, response) } - -func helmReleaseToDownsreamVersion(installedRelease *helm.InstalledRelease) *downstreamtypes.DownstreamVersion { - return &downstreamtypes.DownstreamVersion{ - VersionLabel: installedRelease.Version, - Semver: installedRelease.Semver, - UpdateCursor: installedRelease.Version, - CreatedOn: nil, - DeployedAt: installedRelease.DeployedOn, - UpstreamReleasedAt: installedRelease.ReleasedOn, - IsDeployable: false, // TODO: implement - NonDeployableCause: "already installed", // TODO: implement - HasConfig: true, // TODO: implement - ParentSequence: int64(installedRelease.Revision), - Sequence: int64(installedRelease.Revision), - Status: storetypes.DownstreamVersionStatus(installedRelease.Status.String()), - } -} diff --git a/pkg/handlers/config.go b/pkg/handlers/config.go index deb429784a..32bfa7c857 100644 --- a/pkg/handlers/config.go +++ b/pkg/handlers/config.go @@ -18,7 +18,6 @@ import ( "github.com/replicatedhq/kots/pkg/config" kotsconfig "github.com/replicatedhq/kots/pkg/config" "github.com/replicatedhq/kots/pkg/crypto" - "github.com/replicatedhq/kots/pkg/helm" kotsadmconfig "github.com/replicatedhq/kots/pkg/kotsadmconfig" configtypes "github.com/replicatedhq/kots/pkg/kotsadmconfig/types" configvalidation "github.com/replicatedhq/kots/pkg/kotsadmconfig/validation" @@ -29,7 +28,6 @@ import ( registrytypes "github.com/replicatedhq/kots/pkg/registry/types" "github.com/replicatedhq/kots/pkg/render" rendertypes "github.com/replicatedhq/kots/pkg/render/types" - "github.com/replicatedhq/kots/pkg/replicatedapp" "github.com/replicatedhq/kots/pkg/store" storetypes "github.com/replicatedhq/kots/pkg/store/types" "github.com/replicatedhq/kots/pkg/template" @@ -150,30 +148,6 @@ func (h *Handler) UpdateAppConfig(w http.ResponseWriter, r *http.Request) { return } - if util.IsHelmManaged() { - // TODO: will need to consider flow for when ConfigSpec changes - appSlug := mux.Vars(r)["appSlug"] - - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - requiredItems, requiredItemsTitles := getMissingRequiredConfig(updateAppConfigRequest.ConfigGroups) - if len(requiredItems) > 0 { - updateAppConfigResponse.RequiredItems = requiredItems - updateAppConfigResponse.Error = fmt.Sprintf("The following fields are required: %s", strings.Join(requiredItemsTitles, ", ")) - JSON(w, http.StatusBadRequest, updateAppConfigResponse) - return - } - - helmApp.TempConfigValues = updateAppConfigValues(helmApp.TempConfigValues, updateAppConfigRequest.ConfigGroups) - - JSON(w, http.StatusOK, UpdateAppConfigResponse{Success: true}) - return - } - foundApp, err := store.GetStore().GetAppFromSlug(mux.Vars(r)["appSlug"]) if err != nil { logger.Error(err) @@ -259,128 +233,76 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { var kotsKinds *kotsutil.KotsKinds var nonRenderedConfig *kotsv1beta1.Config var appLicense *kotsv1beta1.License - var app apptypes.AppType var localRegistry registrytypes.RegistrySettings - createNewVersion := false configValues := configValuesFromConfigGroups(liveAppConfigRequest.ConfigGroups) - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - w.WriteHeader(http.StatusNotFound) - return - } - app = helmApp - - isPending, _ := strconv.ParseBool(r.URL.Query().Get("isPending")) - if !isPending { - k, err := helm.GetKotsKindsForRevision(helmApp.Release.Name, liveAppConfigRequest.Sequence, helmApp.Namespace) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get kots kinds for helm")) - w.WriteHeader(http.StatusInternalServerError) - return - } - kotsKinds = &k - nonRenderedConfig = kotsKinds.Config - appLicense = kotsKinds.License - createNewVersion = true - } else { - licenseID := helm.GetKotsLicenseID(&helmApp.Release) - if licenseID == "" { - logger.Error(errors.Errorf("no license and no license ID found for release %s", helmApp.Release.Name)) - w.WriteHeader(http.StatusInternalServerError) - return - } - - k, err := helm.GetKotsKindsFromUpstreamChartVersion(helmApp, licenseID, r.URL.Query().Get("semver")) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get kotskinds from upstream chart")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - licenseData, err := replicatedapp.GetLatestLicenseForHelm(licenseID) - if err != nil { - logger.Error(errors.Wrap(err, "failed to download license for chart archive")) - w.WriteHeader(http.StatusInternalServerError) - return - } - k.License = licenseData.License - - kotsKinds = &k - nonRenderedConfig = kotsKinds.Config - appLicense = kotsKinds.License - } - } else { - foundApp, err := store.GetStore().GetAppFromSlug(appSlug) - if err != nil { - liveAppConfigResponse.Error = "failed to get app from app slug" - logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, liveAppConfigResponse) - return - } - app = foundApp + foundApp, err := store.GetStore().GetAppFromSlug(appSlug) + if err != nil { + liveAppConfigResponse.Error = "failed to get app from app slug" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return + } - appLicense, err = store.GetStore().GetLatestLicenseForApp(foundApp.ID) - if err != nil { - liveAppConfigResponse.Error = "failed to get license for app" - logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, liveAppConfigResponse) - return - } + appLicense, err = store.GetStore().GetLatestLicenseForApp(foundApp.ID) + if err != nil { + liveAppConfigResponse.Error = "failed to get license for app" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return + } - archiveDir, err := os.MkdirTemp("", "kotsadm") - if err != nil { - liveAppConfigResponse.Error = "failed to create temp dir" - logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, liveAppConfigResponse) - return - } - defer os.RemoveAll(archiveDir) + archiveDir, err := os.MkdirTemp("", "kotsadm") + if err != nil { + liveAppConfigResponse.Error = "failed to create temp dir" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return + } + defer os.RemoveAll(archiveDir) - err = store.GetStore().GetAppVersionArchive(foundApp.ID, liveAppConfigRequest.Sequence, archiveDir) - if err != nil { - liveAppConfigResponse.Error = "failed to get app version archive" - logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, liveAppConfigResponse) - return - } + err = store.GetStore().GetAppVersionArchive(foundApp.ID, liveAppConfigRequest.Sequence, archiveDir) + if err != nil { + liveAppConfigResponse.Error = "failed to get app version archive" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return + } - kotsKinds, err = kotsutil.LoadKotsKinds(archiveDir) - if err != nil { - liveAppConfigResponse.Error = "failed to load kots kinds from path" - logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, liveAppConfigResponse) - return - } + kotsKinds, err = kotsutil.LoadKotsKinds(archiveDir) + if err != nil { + liveAppConfigResponse.Error = "failed to load kots kinds from path" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return + } - // get the non-rendered config from the upstream directory because we have to re-render it with the new values - nonRenderedConfig, err = kotsutil.FindConfigInPath(filepath.Join(archiveDir, "upstream")) - if err != nil { - liveAppConfigResponse.Error = "failed to find non-rendered config" - logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, liveAppConfigResponse) - return - } + // get the non-rendered config from the upstream directory because we have to re-render it with the new values + nonRenderedConfig, err = kotsutil.FindConfigInPath(filepath.Join(archiveDir, "upstream")) + if err != nil { + liveAppConfigResponse.Error = "failed to find non-rendered config" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return + } - registryInfo, err := store.GetStore().GetRegistryDetailsForApp(foundApp.ID) - if err != nil { - liveAppConfigResponse.Error = "failed to get app registry info" - logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, liveAppConfigResponse) - return - } + registryInfo, err := store.GetStore().GetRegistryDetailsForApp(foundApp.ID) + if err != nil { + liveAppConfigResponse.Error = "failed to get app registry info" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return + } - localRegistry = registryInfo + localRegistry = registryInfo - createNewVersion, err = shouldCreateNewAppVersion(archiveDir, foundApp.GetID(), liveAppConfigRequest.Sequence) - if err != nil { - liveAppConfigResponse.Error = "failed to check new version" - logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, liveAppConfigResponse) - return - } + createNewVersion, err := shouldCreateNewAppVersion(archiveDir, foundApp.GetID(), liveAppConfigRequest.Sequence) + if err != nil { + liveAppConfigResponse.Error = "failed to check new version" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return } sequence := liveAppConfigRequest.Sequence @@ -388,9 +310,9 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { sequence += 1 } - versionInfo := template.VersionInfoFromInstallationSpec(sequence, app.GetIsAirgap(), kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) - appInfo := template.ApplicationInfo{Slug: app.GetSlug()} - renderedConfig, err := kotsconfig.TemplateConfigObjects(nonRenderedConfig, configValues, appLicense, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, app.GetNamespace(), false) + versionInfo := template.VersionInfoFromInstallationSpec(sequence, foundApp.IsAirgap, kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) + appInfo := template.ApplicationInfo{Slug: foundApp.Slug} + renderedConfig, err := kotsconfig.TemplateConfigObjects(nonRenderedConfig, configValues, appLicense, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, foundApp.GetNamespace(), false) if err != nil { liveAppConfigResponse.Error = "failed to render templates" logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) @@ -484,168 +406,93 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { var nonRenderedConfig *kotsv1beta1.Config var license *kotsv1beta1.License var localRegistry registrytypes.RegistrySettings - var app apptypes.AppType var downstreamVersion *downstreamtypes.DownstreamVersion - createNewVersion := false - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - w.WriteHeader(http.StatusNotFound) - return - } - app = helmApp - - isPending, _ := strconv.ParseBool(r.URL.Query().Get("isPending")) - if !isPending { - installedRelease, err := helm.GetChartVersion(helmApp.Release.Name, sequence, helmApp.Namespace) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get helm release")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if installedRelease == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - downstreamVersion = helmReleaseToDownsreamVersion(installedRelease) - - k, err := helm.GetKotsKindsForRevision(helmApp.Release.Name, sequence, helmApp.Namespace) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get kots kinds for helm")) - w.WriteHeader(http.StatusInternalServerError) - return - } - kotsKinds = &k - nonRenderedConfig = kotsKinds.Config - license = kotsKinds.License - createNewVersion = true - } else { - appKotsKinds, err := helm.GetKotsKindsFromHelmApp(helmApp) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get app kotskinds")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - licenseID := helm.GetKotsLicenseID(&helmApp.Release) - if licenseID == "" { - logger.Error(errors.Errorf("no license and no license ID found for release %s", helmApp.Release.Name)) - w.WriteHeader(http.StatusInternalServerError) - return - } - - k, err := helm.GetKotsKindsFromUpstreamChartVersion(helmApp, licenseID, r.URL.Query().Get("semver")) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get kotskinds from upstream chart")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - k.ConfigValues = appKotsKinds.ConfigValues.DeepCopy() - - licenseData, err := replicatedapp.GetLatestLicenseForHelm(licenseID) - if err != nil { - logger.Error(errors.Wrap(err, "failed to download license for chart archive")) - w.WriteHeader(http.StatusInternalServerError) - return - } - k.License = licenseData.License - - kotsKinds = &k - nonRenderedConfig = kotsKinds.Config - license = kotsKinds.License - } - } else { - foundApp, err := store.GetStore().GetAppFromSlug(appSlug) - if err != nil { - currentAppConfigResponse.Error = "failed to get app from app slug" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } - app = foundApp - - status, err := store.GetStore().GetDownstreamVersionStatus(foundApp.ID, sequence) - if err != nil { - currentAppConfigResponse.Error = "failed to get downstream version status" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } - if status == storetypes.VersionPendingDownload { - err := errors.Errorf("not returning config for version %d because it's %s", sequence, status) - logger.Error(err) - currentAppConfigResponse.Error = err.Error() - JSON(w, http.StatusBadRequest, currentAppConfigResponse) - return - } + foundApp, err := store.GetStore().GetAppFromSlug(appSlug) + if err != nil { + currentAppConfigResponse.Error = "failed to get app from app slug" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } - license, err = store.GetStore().GetLatestLicenseForApp(foundApp.ID) - if err != nil { - currentAppConfigResponse.Error = "failed to get license for app" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } + status, err := store.GetStore().GetDownstreamVersionStatus(foundApp.ID, sequence) + if err != nil { + currentAppConfigResponse.Error = "failed to get downstream version status" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } + if status == storetypes.VersionPendingDownload { + err := errors.Errorf("not returning config for version %d because it's %s", sequence, status) + logger.Error(err) + currentAppConfigResponse.Error = err.Error() + JSON(w, http.StatusBadRequest, currentAppConfigResponse) + return + } - archiveDir, err := os.MkdirTemp("", "kotsadm") - if err != nil { - currentAppConfigResponse.Error = "failed to create temp dir" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } - defer os.RemoveAll(archiveDir) + license, err = store.GetStore().GetLatestLicenseForApp(foundApp.ID) + if err != nil { + currentAppConfigResponse.Error = "failed to get license for app" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } - err = store.GetStore().GetAppVersionArchive(foundApp.ID, sequence, archiveDir) - if err != nil { - currentAppConfigResponse.Error = "failed to get app version archive" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } + archiveDir, err := os.MkdirTemp("", "kotsadm") + if err != nil { + currentAppConfigResponse.Error = "failed to create temp dir" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } + defer os.RemoveAll(archiveDir) - kotsKinds, err = kotsutil.LoadKotsKinds(archiveDir) - if err != nil { - currentAppConfigResponse.Error = "failed to load kots kinds from path" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } + err = store.GetStore().GetAppVersionArchive(foundApp.ID, sequence, archiveDir) + if err != nil { + currentAppConfigResponse.Error = "failed to get app version archive" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } - // get the non-rendered config from the upstream directory because we have to re-render it with the new values - nonRenderedConfig, err = kotsutil.FindConfigInPath(filepath.Join(archiveDir, "upstream")) - if err != nil { - currentAppConfigResponse.Error = "failed to find non-rendered config" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } + kotsKinds, err = kotsutil.LoadKotsKinds(archiveDir) + if err != nil { + currentAppConfigResponse.Error = "failed to load kots kinds from path" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } - registryInfo, err := store.GetStore().GetRegistryDetailsForApp(foundApp.ID) - if err != nil { - currentAppConfigResponse.Error = "failed to get app registry info" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } + // get the non-rendered config from the upstream directory because we have to re-render it with the new values + nonRenderedConfig, err = kotsutil.FindConfigInPath(filepath.Join(archiveDir, "upstream")) + if err != nil { + currentAppConfigResponse.Error = "failed to find non-rendered config" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } - localRegistry = registryInfo + registryInfo, err := store.GetStore().GetRegistryDetailsForApp(foundApp.ID) + if err != nil { + currentAppConfigResponse.Error = "failed to get app registry info" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } - createNewVersion, err = shouldCreateNewAppVersion(archiveDir, foundApp.GetID(), sequence) - if err != nil { - currentAppConfigResponse.Error = "failed to check new version" - logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) - JSON(w, http.StatusInternalServerError, currentAppConfigResponse) - return - } + localRegistry = registryInfo - // TODO: set downstreamVersion + createNewVersion, err := shouldCreateNewAppVersion(archiveDir, foundApp.GetID(), sequence) + if err != nil { + currentAppConfigResponse.Error = "failed to check new version" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return } + // TODO: set downstreamVersion + // get values from saved app version configValues := map[string]template.ItemValue{} @@ -665,9 +512,9 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { sequence += 1 } - versionInfo := template.VersionInfoFromInstallationSpec(sequence, app.GetIsAirgap(), kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) - appInfo := template.ApplicationInfo{Slug: app.GetSlug()} - renderedConfig, err := kotsconfig.TemplateConfigObjects(nonRenderedConfig, configValues, license, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, app.GetNamespace(), false) + versionInfo := template.VersionInfoFromInstallationSpec(sequence, foundApp.GetIsAirgap(), kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) + appInfo := template.ApplicationInfo{Slug: foundApp.GetSlug()} + renderedConfig, err := kotsconfig.TemplateConfigObjects(nonRenderedConfig, configValues, license, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, foundApp.GetNamespace(), false) if err != nil { logger.Error(err) currentAppConfigResponse.Error = "failed to render templates" @@ -951,7 +798,7 @@ func updateAppConfigValues(values map[string]kotsv1beta1.ConfigValue, configGrou values[item.Name] = v } else if item.Value.Type == multitype.String { updatedValue := item.Value.String() - if item.Type == "password" && !util.IsHelmManaged() { // no encryption in helm mode + if item.Type == "password" { // encrypt using the key // if the decryption succeeds, don't encrypt again _, err := util.DecryptConfigValue(updatedValue) @@ -1182,8 +1029,8 @@ func (h *Handler) SetAppConfigValues(w http.ResponseWriter, r *http.Request) { return } - versionInfo := template.VersionInfoFromInstallationSpec(nextAppSequence, foundApp.IsAirgap, kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) - appInfo := template.ApplicationInfo{Slug: foundApp.Slug} + versionInfo := template.VersionInfoFromInstallationSpec(nextAppSequence, foundApp.GetIsAirgap(), kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) + appInfo := template.ApplicationInfo{Slug: foundApp.GetSlug()} renderedConfig, err := kotsconfig.TemplateConfigObjects(newConfig, configValueMap, kotsKinds.License, &kotsKinds.KotsApplication, registryInfo, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, true) if err != nil { setAppConfigValuesResponse.Error = "failed to render templates" diff --git a/pkg/handlers/dashboard.go b/pkg/handlers/dashboard.go index 744309595a..6f6da0f0df 100644 --- a/pkg/handlers/dashboard.go +++ b/pkg/handlers/dashboard.go @@ -7,10 +7,8 @@ import ( "github.com/gorilla/mux" "github.com/pkg/errors" appstatetypes "github.com/replicatedhq/kots/pkg/appstate/types" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/store" - "github.com/replicatedhq/kots/pkg/util" "github.com/replicatedhq/kots/pkg/version" ) @@ -24,30 +22,6 @@ type GetAppDashboardResponse struct { func (h *Handler) GetAppDashboard(w http.ResponseWriter, r *http.Request) { appSlug := mux.Vars(r)["appSlug"] clusterID := mux.Vars(r)["clusterId"] - appStatus := new(appstatetypes.AppStatus) - if util.IsHelmManaged() { - release := helm.GetHelmApp(appSlug) - if release == nil { - logger.Errorf("release for app: %s does not exist\n", appSlug) - w.WriteHeader(http.StatusNotFound) - return - } - - // we have received informer data - if len(release.Status.ResourceStates) > 0 { - getAppDashboardResponse := GetAppDashboardResponse{ - AppStatus: &release.Status, - Metrics: nil, - PrometheusAddress: "", - } - - JSON(w, 200, getAppDashboardResponse) - return - } - - w.WriteHeader(http.StatusNotFound) - return - } a, err := store.GetStore().GetAppFromSlug(appSlug) if err != nil { @@ -56,7 +30,7 @@ func (h *Handler) GetAppDashboard(w http.ResponseWriter, r *http.Request) { return } - appStatus, err = store.GetStore().GetAppStatus(a.ID) + appStatus, err := store.GetStore().GetAppStatus(a.ID) if err != nil { logger.Error(err) w.WriteHeader(500) diff --git a/pkg/handlers/downstream.go b/pkg/handlers/downstream.go index 0bcc693638..011cb72e46 100644 --- a/pkg/handlers/downstream.go +++ b/pkg/handlers/downstream.go @@ -5,11 +5,8 @@ import ( "strconv" "github.com/gorilla/mux" - "github.com/replicatedhq/kots/pkg/api/downstream/types" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/store" - "github.com/replicatedhq/kots/pkg/util" ) type GetDownstreamOutputResponse struct { @@ -29,47 +26,24 @@ func (h *Handler) GetDownstreamOutput(w http.ResponseWriter, r *http.Request) { appSlug := mux.Vars(r)["appSlug"] clusterID := mux.Vars(r)["clusterId"] sequence, err := strconv.Atoi(mux.Vars(r)["sequence"]) - output := new(types.DownstreamOutput) if err != nil { logger.Error(err) w.WriteHeader(http.StatusInternalServerError) return } - if util.IsHelmManaged() { - app := helm.GetHelmApp(appSlug) - if app == nil { - w.WriteHeader(http.StatusNotFound) - return - } - releaseSecret, err := helm.GetChartSecret(app.Release.Name, app.Release.Namespace, mux.Vars(r)["sequence"]) - if err != nil { - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if releaseSecret != nil { - if releaseSecret.Info.Status != "failed" { - output.HelmStdout = releaseSecret.Info.Description - } else { - output.HelmStderr = releaseSecret.Info.Description - } - } - } else { - a, err := store.GetStore().GetAppFromSlug(appSlug) - if err != nil { - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } + a, err := store.GetStore().GetAppFromSlug(appSlug) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return + } - output, err = store.GetStore().GetDownstreamOutput(a.ID, clusterID, int64(sequence)) - if err != nil { - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } + output, err := store.GetStore().GetDownstreamOutput(a.ID, clusterID, int64(sequence)) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return } downstreamLogs := DownstreamLogs{ diff --git a/pkg/handlers/handlers.go b/pkg/handlers/handlers.go index 582e9ed8cc..a0118b414e 100644 --- a/pkg/handlers/handlers.go +++ b/pkg/handlers/handlers.go @@ -59,8 +59,6 @@ func RegisterSessionAuthRoutes(r *mux.Router, kotsStore store.Store, handler KOT HandlerFunc(middleware.EnforceAccess(policy.AppSupportbundleRead, handler.DownloadSupportBundle)) // TODO: appSlug r.Name("CollectSupportBundle").Path("/api/v1/troubleshoot/supportbundle/app/{appId}/cluster/{clusterId}/collect").Methods("POST"). HandlerFunc(middleware.EnforceAccess(policy.AppSupportbundleWrite, handler.CollectSupportBundle)) - r.Name("CollectHelmSupportBundle").Path("/api/v1/troubleshoot/supportbundle/app/{appSlug}/collect").Methods("POST"). - HandlerFunc(middleware.EnforceAccess(policy.AppSupportbundleWrite, handler.CollectHelmSupportBundle)) r.Name("ShareSupportBundle").Path("/api/v1/troubleshoot/app/{appSlug}/supportbundle/{bundleId}/share").Methods("POST"). HandlerFunc(middleware.EnforceAccess(policy.AppSupportbundleWrite, handler.ShareSupportBundle)) r.Name("DeleteSupportBundle").Path("/api/v1/troubleshoot/app/{appSlug}/supportbundle/{bundleId}").Methods("DELETE"). @@ -313,12 +311,6 @@ func RegisterSessionAuthRoutes(r *mux.Router, kotsStore store.Store, handler KOT // Password change r.Name("ChangePassword").Path("/api/v1/password/change").Methods("PUT"). HandlerFunc(middleware.EnforceAccess(policy.PasswordChange, handler.ChangePassword)) - - // Helm - r.Name("IsHelmManaged").Path("/api/v1/is-helm-managed").Methods("GET"). - HandlerFunc(middleware.EnforceAccess(policy.IsHelmManaged, handler.IsHelmManaged)) - r.Name("GetAppValuesFile").Path("/api/v1/app/{appSlug}/values/{sequence}").Methods("GET"). - HandlerFunc(middleware.EnforceAccess(policy.GetAppValuesFile, handler.GetAppValuesFile)) } func JSON(w http.ResponseWriter, code int, payload interface{}) { diff --git a/pkg/handlers/handlers_test.go b/pkg/handlers/handlers_test.go index a12dadc083..18804302e8 100644 --- a/pkg/handlers/handlers_test.go +++ b/pkg/handlers/handlers_test.go @@ -176,17 +176,6 @@ var HandlerPolicyTests = map[string][]HandlerPolicyTest{ ExpectStatus: http.StatusOK, }, }, - "CollectHelmSupportBundle": { - { - Vars: map[string]string{"appSlug": "my-app"}, - Roles: []rbactypes.Role{rbac.ClusterAdminRole}, - SessionRoles: []string{rbac.ClusterAdminRoleID}, - Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) { - handlerRecorder.CollectHelmSupportBundle(gomock.Any(), gomock.Any()) - }, - ExpectStatus: http.StatusOK, - }, - }, "ShareSupportBundle": { { Vars: map[string]string{"appSlug": "my-app", "bundleId": "234"}, @@ -1383,27 +1372,6 @@ var HandlerPolicyTests = map[string][]HandlerPolicyTest{ ExpectStatus: http.StatusOK, }, }, - "IsHelmManaged": { - { - Roles: []rbactypes.Role{rbac.ClusterAdminRole}, - SessionRoles: []string{rbac.ClusterAdminRoleID}, - Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) { - handlerRecorder.IsHelmManaged(gomock.Any(), gomock.Any()) - }, - ExpectStatus: http.StatusOK, - }, - }, - "GetAppValuesFile": { - { - Vars: map[string]string{"appSlug": "123", "sequence": "1"}, - Roles: []rbactypes.Role{rbac.ClusterAdminRole}, - SessionRoles: []string{rbac.ClusterAdminRoleID}, - Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) { - handlerRecorder.GetAppValuesFile(gomock.Any(), gomock.Any()) - }, - ExpectStatus: http.StatusOK, - }, - }, } type HandlerPolicyTest struct { diff --git a/pkg/handlers/helm.go b/pkg/handlers/helm.go deleted file mode 100644 index a878d7af79..0000000000 --- a/pkg/handlers/helm.go +++ /dev/null @@ -1,197 +0,0 @@ -package handlers - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/gorilla/mux" - "github.com/pkg/errors" - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/helm" - kotshelm "github.com/replicatedhq/kots/pkg/helm" - "github.com/replicatedhq/kots/pkg/kotsutil" - "github.com/replicatedhq/kots/pkg/logger" - "github.com/replicatedhq/kots/pkg/replicatedapp" - "github.com/replicatedhq/kots/pkg/util" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - yaml "github.com/replicatedhq/yaml/v3" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// IsHelmManagedResponse - response body for the is helm managed endpoint -type IsHelmManagedResponse struct { - Success bool `json:"success"` - IsHelmManaged bool `json:"isHelmManaged"` -} - -// IsHelmManaged - report whether or not kots is running in helm managed mode -func (h *Handler) IsHelmManaged(w http.ResponseWriter, r *http.Request) { - helmManagedResponse := IsHelmManagedResponse{ - Success: true, - IsHelmManaged: util.IsHelmManaged(), - } - - JSON(w, http.StatusOK, helmManagedResponse) -} - -func (h *Handler) GetAppValuesFile(w http.ResponseWriter, r *http.Request) { - if !util.IsHelmManaged() { - logger.Errorf("values file can only be dowloaded in Helm managed mode") - w.WriteHeader(http.StatusInternalServerError) - return - } - - appSlug := mux.Vars(r)["appSlug"] - sequence, err := strconv.ParseInt(mux.Vars(r)["sequence"], 10, 64) - if err != nil { - logger.Error(errors.Wrap(err, "failed to parse app sequence")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - var kotsKinds *kotsutil.KotsKinds - var tmplVals map[string]interface{} - var helmChartFile []byte - - isPending, _ := strconv.ParseBool(r.URL.Query().Get("isPending")) - if isPending { - licenseID := helm.GetKotsLicenseID(&helmApp.Release) - if licenseID == "" { - logger.Error(errors.Errorf("no license and no license ID found for release %s", helmApp.Release.Name)) - w.WriteHeader(http.StatusInternalServerError) - return - } - - secret, err := helm.GetReplicatedSecretFromUpstreamChartVersion(helmApp, licenseID, r.URL.Query().Get("semver")) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get replicated secret from upstream chart")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - helmChartFile = secret.Data["chart"] - - k, err := helm.GetKotsKindsFromUpstreamChartVersion(helmApp, licenseID, r.URL.Query().Get("semver")) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get kotskinds from upstream chart")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - licenseData, err := replicatedapp.GetLatestLicenseForHelm(licenseID) - if err != nil { - logger.Error(errors.Wrap(err, "failed to download license for chart archive")) - w.WriteHeader(http.StatusInternalServerError) - return - } - k.License = licenseData.License - - kotsKinds = &k - } else { - replicatedSecret, err := helm.GetReplicatedSecretForRevision(helmApp.Release.Name, sequence, helmApp.Namespace) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get secret")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - helmChartFile = replicatedSecret.Data["chart"] - - k, err := helm.GetKotsKindsForRevision(helmApp.Release.Name, sequence, helmApp.Namespace) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get kots kinds for helm")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - kotsKinds = &k - } - - helmChart, err := kotsutil.LoadV1Beta1HelmChartFromContents(helmChartFile) - if err != nil { - logger.Error(errors.Wrap(err, "failed to parse HelmChart file")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - tmplVals, err = helmChart.Spec.GetReplTmplValues(helmChart.Spec.Values) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get templated values")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - // keeping this assignment out of GetKotsKindsFromHelmApp because this is specific to file download endpoint - kotsKinds.ConfigValues = &kotsv1beta1.ConfigValues{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "kots.io/v1beta1", - Kind: "ConfigValues", - }, - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: helmApp.TempConfigValues, - }, - } - - renderedValues, err := kotshelm.RenderValuesFromConfig(helmApp, kotsKinds, helmChartFile) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get render values from config")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - // get a intersected map containing tmplVals keys with renderedValues values - intersectVals := kotsv1beta1.GetMapIntersect(tmplVals, renderedValues) - - mergedHelmValues, err := kotshelm.GetMergedValues(helmApp.Release.Chart.Values, intersectVals) - if err != nil { - logger.Error(errors.Wrap(err, "failed to merge values with templated values")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if kotsKinds.ConfigValues != nil { - v, err := kotshelm.GetConfigValuesMap(kotsKinds.ConfigValues) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get app config values sub-map")) - w.WriteHeader(http.StatusInternalServerError) - return - } - m, err := kotshelm.GetMergedValues(mergedHelmValues, v) - if err != nil { - logger.Error(errors.Wrap(err, "failed to merge app config to helm values")) - w.WriteHeader(http.StatusInternalServerError) - return - } - mergedHelmValues = m - } - - b, err := yaml.Marshal(mergedHelmValues) - if err != nil { - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/octet-stream") - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s-values.yaml", appSlug)) - w.Header().Set("Content-Length", strconv.Itoa(len(b))) - w.WriteHeader(http.StatusOK) - w.Write(b) -} - -func getCompatibleAppFromHelmApp(helmApp *apptypes.HelmApp) (*apptypes.App, error) { - chartApp, err := helm.ResponseAppFromHelmApp(helmApp) - if err != nil { - return nil, errors.Wrap(err, "failed to convert release to app") - } - - foundApp := &apptypes.App{ID: chartApp.ID, Slug: chartApp.Slug, Name: chartApp.Name} - return foundApp, nil -} diff --git a/pkg/handlers/interface.go b/pkg/handlers/interface.go index f974be4eae..eef23d8939 100644 --- a/pkg/handlers/interface.go +++ b/pkg/handlers/interface.go @@ -20,7 +20,6 @@ type KOTSHandler interface { GetSupportBundleRedactions(w http.ResponseWriter, r *http.Request) // TODO: appSlug DownloadSupportBundle(w http.ResponseWriter, r *http.Request) // TODO: appSlug CollectSupportBundle(w http.ResponseWriter, r *http.Request) - CollectHelmSupportBundle(w http.ResponseWriter, r *http.Request) ShareSupportBundle(w http.ResponseWriter, r *http.Request) DeleteSupportBundle(w http.ResponseWriter, r *http.Request) GetPodDetailsFromSupportBundle(w http.ResponseWriter, r *http.Request) @@ -160,8 +159,4 @@ type KOTSHandler interface { // Password change ChangePassword(w http.ResponseWriter, r *http.Request) - - // Helm - IsHelmManaged(w http.ResponseWriter, r *http.Request) - GetAppValuesFile(w http.ResponseWriter, r *http.Request) } diff --git a/pkg/handlers/license.go b/pkg/handlers/license.go index dbac623b63..cc28386f00 100644 --- a/pkg/handlers/license.go +++ b/pkg/handlers/license.go @@ -12,7 +12,6 @@ import ( "github.com/gorilla/mux" "github.com/pkg/errors" apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/k8sutil" "github.com/replicatedhq/kots/pkg/kotsadm" kotsadmtypes "github.com/replicatedhq/kots/pkg/kotsadm/types" @@ -118,80 +117,42 @@ func (h *Handler) SyncLicense(w http.ResponseWriter, r *http.Request) { } appSlug := mux.Vars(r)["appSlug"] - var latestLicense *kotsv1beta1.License - var foundApp *apptypes.App - var err error - var isSynced bool - - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - syncLicenseResponse.Error = "failed to get helm release for slug" - logger.Errorf(syncLicenseResponse.Error) - JSON(w, http.StatusNotFound, syncLicenseResponse) - return - } - - isSynced, err = helm.SyncLicense(helmApp) - if err != nil { - syncLicenseResponse.Error = "failed to sync helm license" - logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, syncLicenseResponse) - return - } - - latestLicense, err = helm.GetChartLicenseFromSecretOrDownload(helmApp) - if err != nil { - syncLicenseResponse.Error = "failed to get license from secret" - logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, syncLicenseResponse) - return - } - foundApp, err = getCompatibleAppFromHelmApp(helmApp) - if err != nil { - syncLicenseResponse.Error = "failed to get app for helm app" - logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, syncLicenseResponse) - return - } - } else { - foundApp, err = store.GetStore().GetAppFromSlug(appSlug) - if err != nil { - syncLicenseResponse.Error = "failed to get app from slug" - logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, syncLicenseResponse) - return - } + foundApp, err := store.GetStore().GetAppFromSlug(appSlug) + if err != nil { + syncLicenseResponse.Error = "failed to get app from slug" + logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) + JSON(w, http.StatusInternalServerError, syncLicenseResponse) + return + } - currentLicense, err := store.GetStore().GetLatestLicenseForApp(foundApp.ID) - if err != nil { - syncLicenseResponse.Error = "failed to get current license" - logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, syncLicenseResponse) - return - } + currentLicense, err := store.GetStore().GetLatestLicenseForApp(foundApp.ID) + if err != nil { + syncLicenseResponse.Error = "failed to get current license" + logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) + JSON(w, http.StatusInternalServerError, syncLicenseResponse) + return + } - latestLicense, isSynced, err = license.Sync(foundApp, syncLicenseRequest.LicenseData, true) - if err != nil { - syncLicenseResponse.Error = "failed to sync license" - logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, syncLicenseResponse) - return - } + latestLicense, isSynced, err := license.Sync(foundApp, syncLicenseRequest.LicenseData, true) + if err != nil { + syncLicenseResponse.Error = "failed to sync license" + logger.Error(errors.Wrap(err, syncLicenseResponse.Error)) + JSON(w, http.StatusInternalServerError, syncLicenseResponse) + return + } - if !foundApp.IsAirgap && currentLicense.Spec.ChannelID != latestLicense.Spec.ChannelID { - // channel changed and this is an online installation, fetch the latest release for the new channel - go func(appID string) { - opts := updatechecker.CheckForUpdatesOpts{ - AppID: appID, - } - _, err := updatechecker.CheckForUpdates(opts) - if err != nil { - logger.Error(errors.Wrap(err, "failed to fetch the latest release for the new channel")) - } - }(foundApp.ID) - } + if !foundApp.IsAirgap && currentLicense.Spec.ChannelID != latestLicense.Spec.ChannelID { + // channel changed and this is an online installation, fetch the latest release for the new channel + go func(appID string) { + opts := updatechecker.CheckForUpdatesOpts{ + AppID: appID, + } + _, err := updatechecker.CheckForUpdates(opts) + if err != nil { + logger.Error(errors.Wrap(err, "failed to fetch the latest release for the new channel")) + } + }(foundApp.ID) } licenseResponse, err := licenseResponseFromLicense(latestLicense, foundApp) @@ -215,56 +176,21 @@ func (h *Handler) GetLicense(w http.ResponseWriter, r *http.Request) { } appSlug := mux.Vars(r)["appSlug"] - license := new(kotsv1beta1.License) - foundApp := new(apptypes.App) - var err error - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - getLicenseResponse.Error = "failed to get helm release for slug" - logger.Errorf(getLicenseResponse.Error) - JSON(w, http.StatusNotFound, getLicenseResponse) - return - } - - currentLicense, err := helm.GetChartLicenseFromSecretOrDownload(helmApp) - if err != nil { - getLicenseResponse.Error = "failed to get license from secret" - logger.Error(errors.Wrap(err, getLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, getLicenseResponse) - return - } - - if currentLicense == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - license = currentLicense - foundApp, err = getCompatibleAppFromHelmApp(helmApp) - if err != nil { - getLicenseResponse.Error = "failed to get app for helm app" - logger.Error(errors.Wrap(err, getLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, getLicenseResponse) - return - } - } else { - foundApp, err = store.GetStore().GetAppFromSlug(appSlug) - if err != nil { - getLicenseResponse.Error = "failed to get app from slug" - logger.Error(errors.Wrap(err, getLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, getLicenseResponse) - return - } + foundApp, err := store.GetStore().GetAppFromSlug(appSlug) + if err != nil { + getLicenseResponse.Error = "failed to get app from slug" + logger.Error(errors.Wrap(err, getLicenseResponse.Error)) + JSON(w, http.StatusInternalServerError, getLicenseResponse) + return + } - license, err = store.GetStore().GetLatestLicenseForApp(foundApp.ID) - if err != nil { - getLicenseResponse.Error = "failed to get license for app" - logger.Error(errors.Wrap(err, getLicenseResponse.Error)) - JSON(w, http.StatusInternalServerError, getLicenseResponse) - return - } + license, err := store.GetStore().GetLatestLicenseForApp(foundApp.ID) + if err != nil { + getLicenseResponse.Error = "failed to get license for app" + logger.Error(errors.Wrap(err, getLicenseResponse.Error)) + JSON(w, http.StatusInternalServerError, getLicenseResponse) + return } licenseResponse, err := licenseResponseFromLicense(license, foundApp) diff --git a/pkg/handlers/metadata.go b/pkg/handlers/metadata.go index 013e5beb1e..1b5d0e4d69 100644 --- a/pkg/handlers/metadata.go +++ b/pkg/handlers/metadata.go @@ -107,9 +107,7 @@ func GetMetadataHandler(getK8sInfoFn MetadataK8sFn, kotsStore store.Store) http. } application := obj.(*kotsv1beta1.Application) metadataResponse.IconURI = application.Spec.Icon - if !util.IsHelmManaged() { - metadataResponse.Branding = getBrandingResponse(kotsStore, appID) - } + metadataResponse.Branding = getBrandingResponse(kotsStore, appID) metadataResponse.Name = application.Spec.Title metadataResponse.UpstreamURI = brandingConfigMap.Data[upstreamUriKey] metadataResponse.ConsoleFeatureFlags = application.Spec.ConsoleFeatureFlags diff --git a/pkg/handlers/mock/mock.go b/pkg/handlers/mock/mock.go index 1c56f5c7ba..3ffdb10508 100644 --- a/pkg/handlers/mock/mock.go +++ b/pkg/handlers/mock/mock.go @@ -130,18 +130,6 @@ func (mr *MockKOTSHandlerMockRecorder) CheckAirgapBundleChunk(w, r interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckAirgapBundleChunk", reflect.TypeOf((*MockKOTSHandler)(nil).CheckAirgapBundleChunk), w, r) } -// CollectHelmSupportBundle mocks base method. -func (m *MockKOTSHandler) CollectHelmSupportBundle(w http.ResponseWriter, r *http.Request) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "CollectHelmSupportBundle", w, r) -} - -// CollectHelmSupportBundle indicates an expected call of CollectHelmSupportBundle. -func (mr *MockKOTSHandlerMockRecorder) CollectHelmSupportBundle(w, r interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CollectHelmSupportBundle", reflect.TypeOf((*MockKOTSHandler)(nil).CollectHelmSupportBundle), w, r) -} - // CollectSupportBundle mocks base method. func (m *MockKOTSHandler) CollectSupportBundle(w http.ResponseWriter, r *http.Request) { m.ctrl.T.Helper() @@ -634,18 +622,6 @@ func (mr *MockKOTSHandlerMockRecorder) GetAppStatus(w, r interface{}) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppStatus", reflect.TypeOf((*MockKOTSHandler)(nil).GetAppStatus), w, r) } -// GetAppValuesFile mocks base method. -func (m *MockKOTSHandler) GetAppValuesFile(w http.ResponseWriter, r *http.Request) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "GetAppValuesFile", w, r) -} - -// GetAppValuesFile indicates an expected call of GetAppValuesFile. -func (mr *MockKOTSHandlerMockRecorder) GetAppValuesFile(w, r interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppValuesFile", reflect.TypeOf((*MockKOTSHandler)(nil).GetAppValuesFile), w, r) -} - // GetAppVersionDownloadStatus mocks base method. func (m *MockKOTSHandler) GetAppVersionDownloadStatus(w http.ResponseWriter, r *http.Request) { m.ctrl.T.Helper() @@ -1114,18 +1090,6 @@ func (mr *MockKOTSHandlerMockRecorder) InitGitOpsConnection(w, r interface{}) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitGitOpsConnection", reflect.TypeOf((*MockKOTSHandler)(nil).InitGitOpsConnection), w, r) } -// IsHelmManaged mocks base method. -func (m *MockKOTSHandler) IsHelmManaged(w http.ResponseWriter, r *http.Request) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "IsHelmManaged", w, r) -} - -// IsHelmManaged indicates an expected call of IsHelmManaged. -func (mr *MockKOTSHandlerMockRecorder) IsHelmManaged(w, r interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsHelmManaged", reflect.TypeOf((*MockKOTSHandler)(nil).IsHelmManaged), w, r) -} - // ListApps mocks base method. func (m *MockKOTSHandler) ListApps(w http.ResponseWriter, r *http.Request) { m.ctrl.T.Helper() diff --git a/pkg/handlers/troubleshoot.go b/pkg/handlers/troubleshoot.go index 456085ec1e..28239b9fce 100644 --- a/pkg/handlers/troubleshoot.go +++ b/pkg/handlers/troubleshoot.go @@ -11,7 +11,6 @@ import ( "github.com/gorilla/mux" "github.com/pkg/errors" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/reporting" "github.com/replicatedhq/kots/pkg/store" @@ -171,18 +170,14 @@ func (h *Handler) GetSupportBundleFiles(w http.ResponseWriter, r *http.Request) func (h *Handler) ListSupportBundles(w http.ResponseWriter, r *http.Request) { appSlug := mux.Vars(r)["appSlug"] - appIDOrSlug := appSlug - if !util.IsHelmManaged() { - a, err := store.GetStore().GetAppFromSlug(appSlug) - if err != nil { - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return - } - appIDOrSlug = a.ID + a, err := store.GetStore().GetAppFromSlug(appSlug) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return } - supportBundles, err := store.GetStore().ListSupportBundles(appIDOrSlug) + supportBundles, err := store.GetStore().ListSupportBundles(a.ID) if err != nil { logger.Error(err) w.WriteHeader(http.StatusInternalServerError) @@ -233,34 +228,6 @@ func (h *Handler) GetSupportBundleCommand(w http.ResponseWriter, r *http.Request return } - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - response.Command = []string{ - "curl https://krew.sh/support-bundle | bash", - "kubectl support-bundle --load-cluster-specs", - } - - opts := types.TroubleshootOptions{ - Origin: getSupportBundleCommandRequest.Origin, - InCluster: false, - } - - if _, err := supportbundle.CreateSupportBundleDependencies(helmApp, helmApp.GetCurrentSequence(), opts); err != nil { - logger.Error(errors.Wrap(err, "failed to create support bundle spec")) - JSON(w, http.StatusOK, response) - return - } - - response.Command = supportbundle.GetBundleCommand(helmApp.GetSlug()) - JSON(w, http.StatusOK, response) - return - } - response.Command = []string{ "curl https://krew.sh/support-bundle | bash", "kubectl support-bundle --load-cluster-specs", @@ -472,11 +439,6 @@ func (h *Handler) DeleteSupportBundle(w http.ResponseWriter, r *http.Request) { } func (h *Handler) CollectSupportBundle(w http.ResponseWriter, r *http.Request) { - if util.IsHelmManaged() { - w.WriteHeader(http.StatusBadRequest) - return - } - a, err := store.GetStore().GetApp(mux.Vars(r)["appId"]) if err != nil { logger.Error(err) @@ -500,35 +462,6 @@ func (h *Handler) CollectSupportBundle(w http.ResponseWriter, r *http.Request) { JSON(w, http.StatusAccepted, collectSupportBundlesResponse) } -func (h *Handler) CollectHelmSupportBundle(w http.ResponseWriter, r *http.Request) { - appSlug := mux.Vars(r)["appSlug"] - - if !util.IsHelmManaged() { - w.WriteHeader(http.StatusBadRequest) - return - } - - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - bundleID, err := supportbundle.CollectHelm(helmApp) - if err != nil { - logger.Error(errors.Wrap(err, "failed to collect helm support bundle")) - w.WriteHeader(http.StatusInternalServerError) - return - } - - collectSupportBundlesResponse := CollectSupportBundlesResponse{ - ID: bundleID, - Slug: bundleID, - } - - JSON(w, http.StatusAccepted, collectSupportBundlesResponse) -} - // UploadSupportBundle route is UNAUTHENTICATED // This request comes from the `kubectl support-bundle` command. func (h *Handler) UploadSupportBundle(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/handlers/update.go b/pkg/handlers/update.go index ecbe6d8af8..24aa6a48e8 100644 --- a/pkg/handlers/update.go +++ b/pkg/handlers/update.go @@ -15,8 +15,6 @@ import ( "github.com/gorilla/mux" "github.com/pkg/errors" "github.com/replicatedhq/kots/pkg/airgap" - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/k8sutil" "github.com/replicatedhq/kots/pkg/kotsadm" "github.com/replicatedhq/kots/pkg/kurl" @@ -57,25 +55,11 @@ func (h *Handler) AppUpdateCheck(w http.ResponseWriter, r *http.Request) { contentType := strings.Split(r.Header.Get("Content-Type"), ";")[0] contentType = strings.TrimSpace(contentType) - var app apptypes.AppType - var kotsApp *apptypes.App - var err error - - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(appSlug) - if helmApp == nil { - w.WriteHeader(http.StatusNotFound) - return - } - app = helmApp - } else { - kotsApp, err = store.GetStore().GetAppFromSlug(appSlug) - if err != nil { - logger.Error(errors.Wrapf(err, "failed to get app for slug %q", appSlug)) - w.WriteHeader(http.StatusNotFound) - return - } - app = kotsApp + app, err := store.GetStore().GetAppFromSlug(appSlug) + if err != nil { + logger.Error(errors.Wrapf(err, "failed to get app for slug %q", appSlug)) + w.WriteHeader(http.StatusNotFound) + return } if contentType == "application/json" { @@ -100,15 +84,12 @@ func (h *Handler) AppUpdateCheck(w http.ResponseWriter, r *http.Request) { return } - if !util.IsHelmManaged() { - // refresh the app to get the correct sequence - kotsApp, err = store.GetStore().GetApp(app.GetID()) - if err != nil { - logger.Error(errors.Wrap(err, "failed to get app")) - w.WriteHeader(http.StatusNotFound) - return - } - app = kotsApp + // refresh the app to get the correct sequence + app, err = store.GetStore().GetApp(app.GetID()) + if err != nil { + logger.Error(errors.Wrap(err, "failed to get app")) + w.WriteHeader(http.StatusNotFound) + return } var appUpdateCheckResponse AppUpdateCheckResponse @@ -147,7 +128,7 @@ func (h *Handler) AppUpdateCheck(w http.ResponseWriter, r *http.Request) { } if contentType == "multipart/form-data" { - if !kotsApp.IsAirgap { + if !app.IsAirgap { logger.Error(errors.New("not an airgap app")) w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Cannot update an online install using an airgap bundle")) @@ -203,7 +184,7 @@ func (h *Handler) AppUpdateCheck(w http.ResponseWriter, r *http.Request) { tasks.StartUpdateTaskMonitor("update-download", finishedChan) - err = airgap.UpdateAppFromPath(kotsApp, rootDir, "", deploy, skipPreflights, skipCompatibilityCheck) + err = airgap.UpdateAppFromPath(app, rootDir, "", deploy, skipPreflights, skipCompatibilityCheck) if err != nil { finishedChan <- err diff --git a/pkg/handlers/update_checker_spec.go b/pkg/handlers/update_checker_spec.go index d7417c53b0..5dbc2b163c 100644 --- a/pkg/handlers/update_checker_spec.go +++ b/pkg/handlers/update_checker_spec.go @@ -7,12 +7,10 @@ import ( "github.com/gorilla/mux" "github.com/pkg/errors" apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/store" "github.com/replicatedhq/kots/pkg/updatechecker" - "github.com/replicatedhq/kots/pkg/util" cron "github.com/robfig/cron/v3" ) @@ -42,52 +40,6 @@ func (h *Handler) SetAutomaticUpdatesConfig(w http.ResponseWriter, r *http.Reque return } - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(mux.Vars(r)["appSlug"]) - license, err := helm.GetChartLicenseFromSecretOrDownload(helmApp) - if err != nil { - logger.Error(errors.Wrap(err, updateCheckerSpecResponse.Error)) - JSON(w, http.StatusInternalServerError, updateCheckerSpecResponse) - return - } - if license == nil { - w.WriteHeader(http.StatusNotFound) - return - } - - if helmApp.GetIsAirgap() { - updateCheckerSpecResponse.Error = "airgap scheduled update checks are not supported" - logger.Error(errors.New(updateCheckerSpecResponse.Error)) - JSON(w, http.StatusBadRequest, updateCheckerSpecResponse) - return - } - - // validate cron spec - cronSpec := configureAutomaticUpdatesRequest.UpdateCheckerSpec - if cronSpec != "@never" && cronSpec != "@default" { - _, err := cron.ParseStandard(cronSpec) - if err != nil { - updateCheckerSpecResponse.Error = "failed to parse cron spec" - logger.Error(errors.Wrap(err, updateCheckerSpecResponse.Error)) - JSON(w, http.StatusInternalServerError, updateCheckerSpecResponse) - return - } - } - - helm.SetUpdateCheckSpec(helmApp, cronSpec) - - // reconfigure update checker for the app - if err := updatechecker.Configure(helmApp, cronSpec); err != nil { - updateCheckerSpecResponse.Error = "failed to reconfigure update checker cron job" - logger.Error(errors.Wrap(err, updateCheckerSpecResponse.Error)) - JSON(w, http.StatusInternalServerError, updateCheckerSpecResponse) - return - } - - JSON(w, http.StatusNoContent, "") - return - } - foundApp, err := store.GetStore().GetAppFromSlug(mux.Vars(r)["appSlug"]) if err != nil { updateCheckerSpecResponse.Error = "failed to get app from slug" @@ -166,33 +118,15 @@ func (h *Handler) SetAutomaticUpdatesConfig(w http.ResponseWriter, r *http.Reque func (h *Handler) GetAutomaticUpdatesConfig(w http.ResponseWriter, r *http.Request) { getCheckerSpecResponse := &GetAutomaticUpdatesConfigResponse{} - if util.IsHelmManaged() { - helmApp := helm.GetHelmApp(mux.Vars(r)["appSlug"]) - if helmApp == nil { - JSON(w, http.StatusNotFound, getCheckerSpecResponse) - return - } - - spec, err := helm.GetUpdateCheckSpec(helmApp) - if err != nil { - getCheckerSpecResponse.Error = "failed to get schedule spec map" - logger.Error(errors.Wrap(err, getCheckerSpecResponse.Error)) - JSON(w, http.StatusInternalServerError, getCheckerSpecResponse) - return - } - - getCheckerSpecResponse.UpdateCheckerSpec = spec - } else { - foundApp, err := store.GetStore().GetAppFromSlug(mux.Vars(r)["appSlug"]) - if err != nil { - getCheckerSpecResponse.Error = "failed to get app from slug" - logger.Error(errors.Wrap(err, getCheckerSpecResponse.Error)) - JSON(w, http.StatusInternalServerError, getCheckerSpecResponse) - return - } - getCheckerSpecResponse.UpdateCheckerSpec = foundApp.UpdateCheckerSpec - getCheckerSpecResponse.AutoDeploy = foundApp.AutoDeploy + foundApp, err := store.GetStore().GetAppFromSlug(mux.Vars(r)["appSlug"]) + if err != nil { + getCheckerSpecResponse.Error = "failed to get app from slug" + logger.Error(errors.Wrap(err, getCheckerSpecResponse.Error)) + JSON(w, http.StatusInternalServerError, getCheckerSpecResponse) + return } + getCheckerSpecResponse.UpdateCheckerSpec = foundApp.UpdateCheckerSpec + getCheckerSpecResponse.AutoDeploy = foundApp.AutoDeploy JSON(w, http.StatusOK, getCheckerSpecResponse) } diff --git a/pkg/helm/cache.go b/pkg/helm/cache.go deleted file mode 100644 index 4b872d06ca..0000000000 --- a/pkg/helm/cache.go +++ /dev/null @@ -1,321 +0,0 @@ -package helm - -import ( - "context" - "io/ioutil" - "strconv" - "sync" - "time" - - "github.com/pkg/errors" - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/k8sutil" - "github.com/replicatedhq/kots/pkg/kotsadmconfig" - "github.com/replicatedhq/kots/pkg/logger" - registrytypes "github.com/replicatedhq/kots/pkg/registry/types" - storetypes "github.com/replicatedhq/kots/pkg/store/types" - "github.com/replicatedhq/kots/pkg/util" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - helmrelease "helm.sh/helm/v3/pkg/release" - corev1 "k8s.io/api/core/v1" - kuberneteserrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/watch" -) - -// TODO: Support same releases names in different namespaces -var ( - helmAppCache = map[string]*apptypes.HelmApp{} - tmpValuesRoot string - appCacheLock sync.Mutex -) - -func Init(ctx context.Context) error { - tmpDir, err := ioutil.TempDir("", "helm-values-") - if err != nil { - return errors.Wrap(err, "failed to create temp dir") - } - tmpValuesRoot = tmpDir - - clientSet, err := k8sutil.GetClientset() - if err != nil { - return errors.Wrap(err, "failed to get clientset") - } - - namespacesToWatch := []string{} - if k8sutil.IsKotsadmClusterScoped(ctx, clientSet, util.PodNamespace) { - namespaces, err := clientSet.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) - if err != nil { - return errors.Wrap(err, "failed to get namespaces") - } - for _, ns := range namespaces.Items { - namespacesToWatch = append(namespacesToWatch, ns.Name) - } - } else { - namespacesToWatch = []string{util.PodNamespace} - } - - for _, namespace := range namespacesToWatch { - secretsSelector := labels.SelectorFromSet(map[string]string{"owner": "helm"}).String() - secrets, err := clientSet.CoreV1().Secrets(namespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: secretsSelector, - }) - if err != nil { - if !kuberneteserrors.IsForbidden(err) && !kuberneteserrors.IsNotFound(err) { - logger.Warnf("failed to list secrets for namespace: %s", namespace) - } - continue - } - - initMonitor(clientSet, namespace) - for _, s := range secrets.Items { - if s.Labels == nil || s.Labels["status"] != helmrelease.StatusDeployed.String() { - continue - } - - releaseInfo, err := helmAppFromSecret(&s) - if err != nil { - logger.Errorf("failed to get helm release from secret %s: %v", s.Name, err) - continue - } - if releaseInfo == nil { - continue - } - if releaseInfo.Release.Chart.Values["replicated"] == nil { - continue - } - - AddHelmApp(releaseInfo.Release.Name, releaseInfo) - resumeHelmStatusInformers(releaseInfo.Release.Name) - } - - go func(namespace string) { - err := watchSecrets(ctx, namespace, secretsSelector) - if err != nil { - logger.Errorf("Faied to watch secrets in ns %s and application cache will not be updated: %v", err) - } - }(namespace) - } - - return nil -} - -func GetHelmApp(releaseName string) *apptypes.HelmApp { - appCacheLock.Lock() - defer appCacheLock.Unlock() - - return helmAppCache[releaseName] -} - -func GetCachedHelmApps() []string { - appCacheLock.Lock() - defer appCacheLock.Unlock() - - releases := []string{} - for k, _ := range helmAppCache { - releases = append(releases, k) - } - return releases -} - -func AddHelmApp(releaseName string, helmApp *apptypes.HelmApp) { - appCacheLock.Lock() - defer appCacheLock.Unlock() - helmAppCache[releaseName] = helmApp -} - -func RemoveHelmApp(releaseName string) { - appCacheLock.Lock() - defer appCacheLock.Unlock() - - delete(helmAppCache, releaseName) -} - -func helmAppFromSecret(secret *corev1.Secret) (*apptypes.HelmApp, error) { - helmRelease, err := HelmReleaseFromSecretData(secret.Data["release"]) - if err != nil { - return nil, errors.Wrap(err, "failed to get helm release from secret") - } - - version, err := strconv.ParseInt(secret.Labels["version"], 10, 64) - if err != nil { - return nil, errors.Wrap(err, "failed to parse release version") - } - - helmApp := &apptypes.HelmApp{ - Release: *helmRelease, - Labels: secret.Labels, - Version: version, - CreationTimestamp: secret.CreationTimestamp.Time, - Namespace: secret.Namespace, - TempConfigValues: map[string]kotsv1beta1.ConfigValue{}, - } - - configSecret, err := GetChartConfigSecret(helmApp) - if err != nil && !kuberneteserrors.IsNotFound(err) { - return nil, errors.Wrap(err, "failed to get helm config secret") - } - - if configSecret == nil { - return helmApp, nil - } - - _, helmApp.IsConfigurable = configSecret.Data["config"] // TODO: also check if there are any config items - helmApp.ChartPath = string(configSecret.Data["chartPath"]) - - return helmApp, nil -} - -func GetKotsLicenseID(release *helmrelease.Release) string { - if release == nil { - return "" - } - - replValuesInterface := release.Chart.Values["replicated"] - if replValuesInterface == nil { - return "" - } - - replValues, ok := replValuesInterface.(map[string]interface{}) - if !ok { - return "" - } - - licenseIDInterface, ok := replValues["license_id"] - if !ok { - return "" - } - - licenseID, ok := licenseIDInterface.(string) - if !ok { - return "" - } - - return licenseID -} - -func watchSecrets(ctx context.Context, namespace string, labelSelector string) error { - clientSet, err := k8sutil.GetClientset() - if err != nil { - return errors.Wrap(err, "failed to get clientset") - } - - opts := metav1.ListOptions{ - LabelSelector: labelSelector, - Watch: true, - } - - secrets := clientSet.CoreV1().Secrets(namespace) - for { - w, err := secrets.Watch(ctx, opts) - if err != nil { - logger.Warnf("failed to list secrets %s for namespace %s: %v", labelSelector, namespace, err) - time.Sleep(time.Second * 20) - continue - } - logger.Debugf("watching for changes to secrets in ns %s", namespace) - for e := range w.ResultChan() { - switch e.Type { - case watch.Added, watch.Modified: - secret, ok := e.Object.(*corev1.Secret) - if !ok { - break - } - logger.Debugf("got event %s for secret %s in ns %s", e.Type, secret.Name, namespace) - if secret.Labels == nil || secret.Labels["status"] != helmrelease.StatusDeployed.String() { - continue - } - helmApp, err := helmAppFromSecret(secret) - if err != nil { - logger.Errorf("failed to create helm release info from secret %s in namespace %s: %s", secret.Name, namespace) - break - } - if helmApp == nil { - break - } - if helmApp.Release.Chart.Values["replicated"] == nil { - break - } - - if err := recalculateCachedUpdates(helmApp); err != nil { - logger.Errorf("failed to recalculate updates for helm release info from secret %s in namespace %s: %s", secret.Name, namespace, err) - // Continue here. Release has been installed and should show up up in Admin Console. - } - - logger.Debugf("adding secret %s to cache", secret.Name) - AddHelmApp(helmApp.Release.Name, helmApp) - resumeHelmStatusInformers(helmApp.Release.Name) - case watch.Deleted: - secret, ok := e.Object.(*corev1.Secret) - if !ok { - break - } - - helmRelease, err := HelmReleaseFromSecretData(secret.Data["release"]) - if err != nil { - logger.Errorf("failed to get helm release from secret in delete event", err) - break - } - - // Get app from cache because the config secret is likely gone now, and we can't construct this data from cluster - helmApp := GetHelmApp(helmRelease.Name) - if helmApp == nil { - break - } - - deleteUpdateCacheForChart(helmApp.ChartPath) - - RemoveHelmApp(helmApp.Release.Name) - default: - secret, ok := e.Object.(*corev1.Secret) - if !ok { - break - } - logger.Debugf("%v event ignored for secret %s in namespace %s", e.Type, secret.Name, namespace) - } - } - logger.Infof("watch of secrets in ns %s unexpectedly terminated. Reconnecting...\n", namespace) - time.Sleep(time.Second * 5) - } -} - -func recalculateCachedUpdates(newHelmApp *apptypes.HelmApp) error { - removeFromCachedUpdates(newHelmApp.ChartPath, newHelmApp.Release.Chart.Metadata.Version) - - currentHelmApp := GetHelmApp(newHelmApp.Release.Name) - if currentHelmApp == nil { - // no app installed yet - return nil - } - updates := GetCachedUpdates(currentHelmApp.ChartPath) - currentKotsKinds, err := GetKotsKindsFromHelmApp(currentHelmApp) - if err != nil { - return errors.Wrapf(err, "failed to get current config values") - } - - // Check if new configuration has been applied and now required items have been set in pending updates. - for _, update := range updates { - if update.Status != storetypes.VersionPendingConfig { - continue - } - - kotsKinds, err := GetKotsKindsFromUpstreamChartVersion(currentHelmApp, currentKotsKinds.License.Spec.LicenseID, update.Tag) - if err != nil { - return errors.Wrapf(err, "failed to pull update %s for chart", update.Version) - } - kotsKinds.ConfigValues = currentKotsKinds.ConfigValues.DeepCopy() - - sequence := int64(-1) // TODO: do something sensible, this value isn't used - registrySettings := registrytypes.RegistrySettings{} // TODO: private registries aren't supported yet - t, err := kotsadmconfig.NeedsConfiguration(currentHelmApp.GetSlug(), sequence, currentHelmApp.GetIsAirgap(), &kotsKinds, registrySettings) - if err != nil { - return errors.Wrap(err, "failed to check if version needs configuration") - } - if !t { - SetCachedUpdateStatus(currentHelmApp.ChartPath, update.Tag, storetypes.VersionPending) - } - } - - return nil -} diff --git a/pkg/helm/charts.go b/pkg/helm/charts.go deleted file mode 100644 index 63a6f6e6eb..0000000000 --- a/pkg/helm/charts.go +++ /dev/null @@ -1,582 +0,0 @@ -package helm - -import ( - "bytes" - "compress/gzip" - "context" - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "sort" - "strconv" - "sync" - "text/template" - "time" - - "github.com/blang/semver" - "github.com/pkg/errors" - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/k8sutil" - "github.com/replicatedhq/kots/pkg/kotsutil" - "github.com/replicatedhq/kots/pkg/logger" - "github.com/replicatedhq/kots/pkg/replicatedapp" - "github.com/replicatedhq/kots/pkg/util" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - kotsscheme "github.com/replicatedhq/kotskinds/client/kotsclientset/scheme" - helmrelease "helm.sh/helm/v3/pkg/release" - corev1 "k8s.io/api/core/v1" - kuberneteserrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/kubernetes/scheme" -) - -var ( - configSecretMutex sync.Mutex -) - -// Secret labels from Helm v3 code: -// -// lbs.set("name", rls.Name) -// lbs.set("owner", owner) -// lbs.set("status", rls.Info.Status.String()) -// lbs.set("version", strconv.Itoa(rls.Version)) -type InstalledRelease struct { - ReleaseName string - Revision int - Version string - Semver *semver.Version - Status helmrelease.Status - DeployedOn *time.Time - ReleasedOn *time.Time -} - -type InstalledReleases []InstalledRelease - -func (v InstalledReleases) Len() int { - return len(v) -} - -func (v InstalledReleases) Swap(i, j int) { - v[i], v[j] = v[j], v[i] -} - -func (v InstalledReleases) Less(i, j int) bool { - return v[i].Version < v[j].Version -} - -func GetChartSecret(releaseName, namespace, version string) (*helmrelease.Release, error) { - clientSet, err := k8sutil.GetClientset() - if err != nil { - return nil, errors.Wrap(err, "failed to get clientset") - } - - selectorLabels := map[string]string{ - "owner": "helm", - "name": releaseName, - "version": version, - } - listOpts := metav1.ListOptions{ - LabelSelector: labels.SelectorFromSet(selectorLabels).String(), - } - - secrets, err := clientSet.CoreV1().Secrets(namespace).List(context.TODO(), listOpts) - if err != nil { - if kuberneteserrors.IsNotFound(err) { - return nil, nil - } - return nil, errors.Wrap(err, "failed to list secrets") - } - if len(secrets.Items) > 1 { - return nil, errors.New("found multiple secrets for single release revision") - } - - helmRelease, err := HelmReleaseFromSecretData(secrets.Items[0].Data["release"]) - if err != nil { - return nil, errors.Wrap(err, "failed to parse release info from secret") - } - - return helmRelease, nil -} - -func ListChartVersions(releaseName string, namespace string) ([]InstalledRelease, error) { - clientSet, err := k8sutil.GetClientset() - if err != nil { - return nil, errors.Wrap(err, "failed to get clientset") - } - - selectorLabels := map[string]string{ - "owner": "helm", - "name": releaseName, - } - listOpts := metav1.ListOptions{ - LabelSelector: labels.SelectorFromSet(selectorLabels).String(), - } - - secrets, err := clientSet.CoreV1().Secrets(namespace).List(context.TODO(), listOpts) - if err != nil { - if kuberneteserrors.IsNotFound(err) { - return InstalledReleases{}, nil - } - return nil, errors.Wrap(err, "failed to list secrets") - } - - releases := InstalledReleases{} - for _, secret := range secrets.Items { - release, err := getChartVersionFromSecretData(&secret) - if err != nil { - logger.Warnf("failed to create release from secret chart %s revision number %v: %v", releaseName, secret.Labels["version"], err) - continue - } - - releases = append(releases, *release) - } - - sort.Sort(sort.Reverse(releases)) - - return releases, nil -} - -func GetChartVersion(releaseName string, revision int64, namespace string) (*InstalledRelease, error) { - clientSet, err := k8sutil.GetClientset() - if err != nil { - return nil, errors.Wrap(err, "failed to get clientset") - } - - selectorLabels := map[string]string{ - "owner": "helm", - "name": releaseName, - "version": fmt.Sprintf("%d", revision), - } - listOpts := metav1.ListOptions{ - LabelSelector: labels.SelectorFromSet(selectorLabels).String(), - } - - secrets, err := clientSet.CoreV1().Secrets(namespace).List(context.TODO(), listOpts) - if err != nil { - return nil, errors.Wrap(err, "failed to list secrets") - } - - if len(secrets.Items) == 0 { - return nil, nil - } - - if len(secrets.Items) != 1 { - return nil, errors.Errorf("found %d secrets but expected 1", len(secrets.Items)) - } - - release, err := getChartVersionFromSecretData(&secrets.Items[0]) - if err != nil { - return nil, errors.Wrap(err, "failed to create release from secret") - } - - return release, nil -} - -func getChartVersionFromSecretData(secret *corev1.Secret) (*InstalledRelease, error) { - revision, err := strconv.Atoi(secret.Labels["version"]) - if err != nil { - return nil, errors.Wrap(err, "failed to parse release version") - } - - helmRelease, err := HelmReleaseFromSecretData(secret.Data["release"]) - if err != nil { - return nil, errors.Wrap(err, "failed to parse release data") - } - - release := InstalledRelease{ - ReleaseName: secret.Labels["releaseName"], - Revision: revision, - Status: helmrelease.Status(secret.Labels["status"]), - DeployedOn: &secret.CreationTimestamp.Time, - } - - createdAt := util.GetValueFromMapPath(helmRelease.Chart.Values, []string{"replicated", "created_at"}) - if s, ok := createdAt.(string); ok { - t, err := time.Parse(time.RFC3339, s) - if err == nil { - release.ReleasedOn = &t - } - } - - if helmRelease.Chart != nil && helmRelease.Chart.Metadata != nil { - release.Version = helmRelease.Chart.Metadata.Version - } - - sv, err := semver.ParseTolerant(release.Version) - if err != nil { - return nil, errors.Wrap(err, "failed to parse release version") - } - release.Semver = &sv - - return &release, nil -} - -func HelmReleaseFromSecretData(data []byte) (*helmrelease.Release, error) { - base64Reader := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(data)) - gzreader, err := gzip.NewReader(base64Reader) - if err != nil { - return nil, errors.Wrap(err, "failed to create gzip reader") - } - defer gzreader.Close() - - releaseData, err := ioutil.ReadAll(gzreader) - if err != nil { - return nil, errors.Wrap(err, "failed to read from gzip reader") - } - - release := &helmrelease.Release{} - err = json.Unmarshal(releaseData, &release) - if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal release data") - } - - return release, nil -} - -func GetChartConfigSecret(helmApp *apptypes.HelmApp) (*corev1.Secret, error) { - clientSet, err := k8sutil.GetClientset() - if err != nil { - return nil, errors.Wrap(err, "failed to get clientset") - } - - // Note that this is release name - chart name to support deploying multiple instances - secretName := fmt.Sprintf("kots-%s-%s-config", helmApp.Release.Chart.Name(), helmApp.Release.Name) - secret, err := clientSet.CoreV1().Secrets(helmApp.Namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) - if err != nil { - if kuberneteserrors.IsNotFound(err) { - return nil, nil - } - return nil, errors.Wrap(err, "failed to get secret") - } - - return secret, nil -} - -func GetChartLicenseFromSecretOrDownload(helmApp *apptypes.HelmApp) (*kotsv1beta1.License, error) { - configSecret, err := GetChartConfigSecret(helmApp) - if err != nil { - return nil, errors.Wrap(err, "failed to get helm config secret") - } - - if configSecret == nil { - return nil, errors.Errorf("no config secret found for release %s", helmApp.Release.Name) - } - - if licenseData := configSecret.Data["license"]; len(licenseData) > 0 { - decode := kotsscheme.Codecs.UniversalDeserializer().Decode - obj, gvk, err := decode(licenseData, nil, nil) - if err != nil { - return nil, errors.Wrap(err, "failed to decode license data") - } - - if gvk.Group != "kots.io" || gvk.Version != "v1beta1" || gvk.Kind != "License" { - return nil, errors.Errorf("unexpected GVK: %s", gvk.String()) - } - - return obj.(*kotsv1beta1.License), nil - } - - license, err := downloadAppLicense(helmApp) - if err != nil { - return nil, errors.Wrap(err, "failed to download app license") - } - - return license, nil -} - -func downloadAppLicense(helmApp *apptypes.HelmApp) (*kotsv1beta1.License, error) { - licenseID := GetKotsLicenseID(&helmApp.Release) - if licenseID == "" { - return nil, errors.Errorf("no license and no license ID found for release %s", helmApp.Release.Name) - } - - licenseData, err := replicatedapp.GetLatestLicenseForHelm(licenseID) - if err != nil { - return nil, errors.Wrapf(err, "failed to get license for helm chart %s", helmApp.Release.Name) - } - - if err := SaveChartLicenseInSecret(helmApp, licenseData.LicenseBytes); err != nil { - return nil, errors.Wrapf(err, "failed save license in config for chart %s", helmApp.Release.Name) - } - - return licenseData.License, nil -} - -// Always save original data returned from the server without remarshaling. -func SaveChartLicenseInSecret(helmApp *apptypes.HelmApp, licenseData []byte) error { - configSecretMutex.Lock() - defer configSecretMutex.Unlock() - - secret, err := GetChartConfigSecret(helmApp) - if err != nil { - return errors.Wrap(err, "failed to get config secret for license update") - } - - if secret == nil { - return errors.Errorf("secret not found") - } - - secret.Data["license"] = licenseData - - clientSet, err := k8sutil.GetClientset() - if err != nil { - return errors.Wrap(err, "failed to get clientset") - } - - _, err = clientSet.CoreV1().Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}) - if err != nil { - // TODO: retry on IsConflict - return errors.Wrap(err, "failed to update secret") - } - - return nil -} - -func GetKotsKindsFromHelmApp(helmApp *apptypes.HelmApp) (kotsutil.KotsKinds, error) { - kotsKinds := kotsutil.EmptyKotsKinds() - - secret, err := GetChartConfigSecret(helmApp) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to get helm config secret") - } - - if secret == nil { - return kotsKinds, nil - } - - kotsKinds, err = GetKotsKindsFromReplicatedSecret(secret) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to get kots kinds from secret") - } - - if kotsKinds.License == nil { - license, err := downloadAppLicense(helmApp) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to download license") - } - kotsKinds.License = license - } - - return kotsKinds, nil -} - -func GetKotsKindsFromReplicatedSecret(secret *corev1.Secret) (kotsutil.KotsKinds, error) { - kotsKinds := kotsutil.EmptyKotsKinds() - - licenseData := secret.Data["license"] - if len(licenseData) != 0 { - license, err := kotsutil.LoadLicenseFromBytes(licenseData) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to load license from data") - } - kotsKinds.License = license - } - - configData := secret.Data["config"] - if len(configData) != 0 { - config, err := kotsutil.LoadConfigFromBytes(configData) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to load config from data") - } - kotsKinds.Config = config - } - - configValuesData := secret.Data["configValues"] - if len(configValuesData) != 0 { - configValues, err := kotsutil.LoadConfigValuesFromBytes(configValuesData) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to load config values from data") - } - kotsKinds.ConfigValues = configValues - } - - appData := secret.Data["application"] - if len(appData) != 0 { - app, err := kotsutil.LoadApplicationFromBytes(appData) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to load application from data") - } - kotsKinds.KotsApplication = *app - } - - return kotsKinds, nil -} - -func GetKotsKindsForRevision(releaseName string, revision int64, namespace string) (kotsutil.KotsKinds, error) { - kotsKinds := kotsutil.EmptyKotsKinds() - - clientSet, err := k8sutil.GetClientset() - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to get clientset") - } - - selectorLabels := map[string]string{ - "owner": "helm", - "version": fmt.Sprintf("%d", revision), - "name": releaseName, - } - listOpts := metav1.ListOptions{ - LabelSelector: labels.SelectorFromSet(selectorLabels).String(), - } - - secrets, err := clientSet.CoreV1().Secrets(namespace).List(context.TODO(), listOpts) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to list secrets") - } - - if len(secrets.Items) != 1 { - return kotsKinds, errors.Errorf("expected to match 1 secret, but found %d", len(secrets.Items)) - } - - chartSecret := secrets.Items[0] - helmApp, err := helmAppFromSecret(&chartSecret) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to convert secret to helm app") - } - - license, err := GetChartLicenseFromSecretOrDownload(helmApp) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to get license") - } - kotsKinds.License = license - - // "Config" object is in the template secret. - for _, template := range helmApp.Release.Chart.Templates { - if template.Name != "templates/_replicated/secret.yaml" { - continue - } - - secretData, err := removeHelmTemplate(template.Data) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to remove helm templates from replicated secret file") - } - - decode := scheme.Codecs.UniversalDeserializer().Decode - obj, gvk, err := decode(secretData, nil, nil) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to decode secret data") - } - - if gvk.Group != "" || gvk.Version != "v1" || gvk.Kind != "Secret" { - return kotsKinds, errors.Errorf("unexpected secret GVK: %s", gvk.String()) - } - - k, err := GetKotsKindsFromReplicatedSecret(obj.(*corev1.Secret)) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to get kots kinds from secret") - } - - kotsKinds.Config = k.Config - kotsKinds.Application = k.Application - - break - } - - // If chart was deployed with --values, they will be in Config. Otherwise, get the default values injected by the registry - encodedConfigValues := util.GetValueFromMapPath(helmApp.Release.Config, []string{"replicated", "app", "configValues"}) - if encodedConfigValues == nil { - encodedConfigValues = util.GetValueFromMapPath(helmApp.Release.Chart.Values, []string{"replicated", "app", "configValues"}) - } - - if encodedConfigValues == nil { - return kotsKinds, errors.Errorf("failed to find configValues from release %s", helmApp.Release.Name) - } - - configValuesData, err := util.Base64DecodeInterface(encodedConfigValues) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to base64 decode config values from chart release") - } - - kotsKinds.ConfigValues, err = kotsutil.LoadConfigValuesFromBytes(configValuesData) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to get config values from chart values") - } - - return kotsKinds, nil -} - -func GetReplicatedSecretForRevision(releaseName string, revision int64, namespace string) (*corev1.Secret, error) { - clientSet, err := k8sutil.GetClientset() - if err != nil { - return nil, errors.Wrap(err, "failed to get clientset") - } - - selectorLabels := map[string]string{ - "owner": "helm", - "version": fmt.Sprintf("%d", revision), - "name": releaseName, - } - listOpts := metav1.ListOptions{ - LabelSelector: labels.SelectorFromSet(selectorLabels).String(), - } - - secrets, err := clientSet.CoreV1().Secrets(namespace).List(context.TODO(), listOpts) - if err != nil { - return nil, errors.Wrap(err, "failed to list secrets") - } - - if len(secrets.Items) != 1 { - return nil, errors.Errorf("expected to match 1 secret, but found %d", len(secrets.Items)) - } - - chartSecret := secrets.Items[0] - helmApp, err := helmAppFromSecret(&chartSecret) - if err != nil { - return nil, errors.Wrap(err, "failed to convert secret to helm app") - } - - for _, template := range helmApp.Release.Chart.Templates { - if template.Name != "templates/_replicated/secret.yaml" { - continue - } - - secretData, err := removeHelmTemplate(template.Data) - if err != nil { - return nil, errors.Wrap(err, "failed to remove helm templates from replicated secret file") - } - - decode := scheme.Codecs.UniversalDeserializer().Decode - obj, gvk, err := decode(secretData, nil, nil) - if err != nil { - return nil, errors.Wrap(err, "failed to decode secret data") - } - - if gvk.Group != "" || gvk.Version != "v1" || gvk.Kind != "Secret" { - return nil, errors.Errorf("unexpected secret GVK: %s", gvk.String()) - } - - return obj.(*corev1.Secret), nil - } - - return nil, errors.Errorf("replicated secret template not found for chart %q, revision %d, in ns %q", releaseName, revision, namespace) -} - -func removeHelmTemplate(doc []byte) ([]byte, error) { - type Inventory struct { - Material string - Count uint - } - replicatedValues := map[string]interface{}{ - "Values": map[string]interface{}{ - "replicated": map[string]interface{}{ - "app": map[string]interface{}{ - "configValues": base64.RawStdEncoding.EncodeToString(nil), - }, - }, - }, - } - tmpl, err := template.New("sanitize-helm").Parse(string(doc)) - if err != nil { - return nil, errors.Wrap(err, "failed to parse doc as template") - } - - b := bytes.NewBuffer(nil) - err = tmpl.Execute(b, replicatedValues) - if err != nil { - return nil, errors.Wrap(err, "failed to execute template") - } - - return b.Bytes(), nil -} diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go deleted file mode 100644 index a86320d1b8..0000000000 --- a/pkg/helm/helm.go +++ /dev/null @@ -1,253 +0,0 @@ -package helm - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/blang/semver" - imagedocker "github.com/containers/image/v5/docker" - dockerref "github.com/containers/image/v5/docker/reference" - "github.com/pkg/errors" - downstreamtypes "github.com/replicatedhq/kots/pkg/api/downstream/types" - "github.com/replicatedhq/kots/pkg/api/handlers/types" - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/docker/registry" - "github.com/replicatedhq/kots/pkg/kotsutil" - registrytypes "github.com/replicatedhq/kots/pkg/registry/types" - "github.com/replicatedhq/kots/pkg/render" - storetypes "github.com/replicatedhq/kots/pkg/store/types" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - helmval "helm.sh/helm/v3/pkg/cli/values" - "helm.sh/helm/v3/pkg/helmpath" - helmregistry "helm.sh/helm/v3/pkg/registry" - serializer "k8s.io/apimachinery/pkg/runtime/serializer/json" - "k8s.io/client-go/kubernetes/scheme" -) - -func RenderValuesFromConfig(helmApp *apptypes.HelmApp, kotsKinds *kotsutil.KotsKinds, chart []byte) (map[string]interface{}, error) { - builder, err := render.NewBuilder(kotsKinds, registrytypes.RegistrySettings{}, helmApp.GetSlug(), helmApp.GetCurrentSequence(), helmApp.GetIsAirgap(), helmApp.Namespace) - if err != nil { - return nil, errors.Wrap(err, "failed to make tempalate builder") - } - - renderedHelmManifest, err := builder.RenderTemplate("helm", string(chart)) - if err != nil { - return nil, err - } - - kotsHelmChart, err := kotsutil.LoadV1Beta1HelmChartFromContents([]byte(renderedHelmManifest)) - if err != nil { - return nil, err - } - - mergedValues := kotsHelmChart.Spec.Values - for _, optionalValues := range kotsHelmChart.Spec.OptionalValues { - include, err := strconv.ParseBool(optionalValues.When) - if err != nil { - return nil, err - } - if !include { - continue - } - if optionalValues.RecursiveMerge { - mergedValues = kotsv1beta1.MergeHelmChartValues(mergedValues, optionalValues.Values) - } else { - for k, v := range optionalValues.Values { - mergedValues[k] = v - } - } - } - - renderedValues, err := kotsHelmChart.Spec.GetHelmValues(mergedValues) - if err != nil { - return nil, err - } - - return renderedValues, nil -} - -func GetMergedValues(releasedValues, renderedValues map[string]interface{}) (map[string]interface{}, error) { - dir, err := ioutil.TempDir("", "helm-merged-values-") - if err != nil { - return nil, err - } - defer os.RemoveAll(dir) - - releasedB, err := json.Marshal(releasedValues) - if err != nil { - return nil, err - - } - renderedB, err := json.Marshal(renderedValues) - if err != nil { - return nil, err - } - releaseValsFilename := fmt.Sprintf("%s/releasevalues.yaml", dir) - renderedValsFilename := fmt.Sprintf("%s/renderedvalues.yaml", dir) - if err := ioutil.WriteFile(releaseValsFilename, releasedB, 0644); err != nil { - return nil, err - } - - if err := ioutil.WriteFile(renderedValsFilename, renderedB, 0644); err != nil { - return nil, err - } - - helmopts := &helmval.Options{ValueFiles: []string{releaseValsFilename, renderedValsFilename}} - mergedHelmVals, err := helmopts.MergeValues(nil) - if err != nil { - return nil, err - } - - return mergedHelmVals, nil -} - -func CreateHelmRegistryCreds(username string, password string, url string) error { - url = strings.TrimLeft(url, "oci://") - ref, err := imagedocker.ParseReference(fmt.Sprintf("//%s", url)) - if err != nil { - return errors.Wrapf(err, "failed to parse support bundle ref %q", url) - } - dockerRef := ref.DockerReference() - - registryHost := dockerref.Domain(dockerRef) - - dockercfgAuth := registry.DockercfgAuth{ - Auth: base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))), - } - - dockerCfgJSON := registry.DockerCfgJSON{ - Auths: map[string]registry.DockercfgAuth{ - registryHost: dockercfgAuth, - }, - } - data, err := json.MarshalIndent(dockerCfgJSON, "", " ") - if err != nil { - return errors.Wrap(err, "failed to marshal helm registry credentials") - } - - filename := helmpath.ConfigPath(helmregistry.CredentialsFileBasename) - - err = os.MkdirAll(filepath.Dir(filename), 0700) - if err != nil { - return errors.Wrap(err, "failed to create directory for helm registry credentials") - } - - err = ioutil.WriteFile(filename, data, 0600) - if err != nil { - return errors.Wrap(err, "failed to save helm registry credentials") - } - - return nil -} - -func GetConfigValuesMap(configValues *kotsv1beta1.ConfigValues) (map[string]interface{}, error) { - s := serializer.NewYAMLSerializer(serializer.DefaultMetaFactory, scheme.Scheme, scheme.Scheme) - var configValuesBuffer bytes.Buffer - if err := s.Encode(configValues, &configValuesBuffer); err != nil { - return nil, errors.Wrap(err, "failed to encode config values") - } - - configValuesMap := map[string]interface{}{ - "replicated": map[string]interface{}{ - "app": map[string][]byte{ // "byte" for base64 encoding - "configValues": configValuesBuffer.Bytes(), - }, - }, - } - - return configValuesMap, nil -} - -func HelmUpdateToDownsreamVersion(update ChartUpdate, sequence int64) *downstreamtypes.DownstreamVersion { - return &downstreamtypes.DownstreamVersion{ - VersionLabel: update.Tag, - Semver: &update.Version, - UpdateCursor: update.Tag, - Sequence: sequence, - ParentSequence: sequence, - CreatedOn: update.CreatedOn, - UpstreamReleasedAt: update.CreatedOn, - IsDeployable: false, // TODO: implement - NonDeployableCause: "not implemented", // TODO: implement - HasConfig: true, // TODO: implement - Source: "Upstream Update", - Status: update.Status, - } -} - -func ResponseAppFromHelmApp(helmApp *apptypes.HelmApp) (*types.HelmResponseApp, error) { - unixIntValue, err := strconv.ParseInt(helmApp.Labels["modifiedAt"], 10, 64) - var updatedTs time.Time - if err == nil { - updatedTs = time.Unix(unixIntValue, 0) - } - - sv, err := semver.ParseTolerant(helmApp.Release.Chart.Metadata.Version) - if err != nil { - return nil, errors.Wrap(err, "failed to parse release version into semver") - } - - iconURI := "https://cncf-branding.netlify.app/img/projects/helm/horizontal/color/helm-horizontal-color.png" - // use chart icon if it exists, if not use default helm icon - if helmApp.Release.Chart.Metadata.Icon != "" { - iconURI = helmApp.Release.Chart.Metadata.Icon - } - - revision, err := strconv.Atoi(helmApp.Labels["version"]) - if err != nil { - return nil, errors.Wrap(err, "failed to parse release revision number") - } - - downstreamVersion := &downstreamtypes.DownstreamVersion{ - VersionLabel: helmApp.Release.Chart.Metadata.Version, - Semver: &sv, - Sequence: int64(revision), - ParentSequence: int64(revision), - Status: storetypes.VersionDeployed, - CreatedOn: &helmApp.Release.Info.FirstDeployed.Time, - DeployedAt: &helmApp.Release.Info.LastDeployed.Time, - } - - var username, password string - if replVals := helmApp.Release.Chart.Values["replicated"].(map[string]interface{}); replVals != nil { - username, _ = replVals["username"].(string) - password, _ = replVals["license_id"].(string) - } - - chartUpdates := GetDownloadedUpdates(helmApp.ChartPath) - pendingVersions := make([]*downstreamtypes.DownstreamVersion, len(chartUpdates), len(chartUpdates)) - nextSequence := revision + 1 - for i := len(chartUpdates) - 1; i >= 0; i-- { - pendingVersions[i] = HelmUpdateToDownsreamVersion(chartUpdates[i], int64(nextSequence)) - nextSequence = nextSequence + 1 - } - - return &types.HelmResponseApp{ - ResponseApp: types.ResponseApp{ - Name: helmApp.Labels["name"], - Namespace: helmApp.Namespace, - Slug: helmApp.Labels["name"], - CreatedAt: helmApp.CreationTimestamp, - IsConfigurable: helmApp.IsConfigurable, - UpdatedAt: &updatedTs, - IconURI: iconURI, - Downstream: types.ResponseDownstream{ - CurrentVersion: downstreamVersion, - PendingVersions: pendingVersions, - }, - }, - Credentials: types.Credentials{ - Username: username, - Password: password, - }, - ChartPath: helmApp.ChartPath, - }, nil -} diff --git a/pkg/helm/helm_test.go b/pkg/helm/helm_test.go deleted file mode 100644 index 2c43208d2d..0000000000 --- a/pkg/helm/helm_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package helm - -import ( - "reflect" - "testing" - - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/kotsutil" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - helmrelease "helm.sh/helm/v3/pkg/release" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func Test_GetMergedValues(t *testing.T) { - tests := []struct { - name string - releaseValues map[string]interface{} - renderedValues map[string]interface{} - expectedValues map[string]interface{} - }{ - { - name: "empty values", - releaseValues: map[string]interface{}{}, - renderedValues: map[string]interface{}{}, - expectedValues: map[string]interface{}{}, - }, - { - name: "top level override", - releaseValues: map[string]interface{}{ - "nameOverride": "", - "imageVersion": "0.1.2", - }, - renderedValues: map[string]interface{}{ - "nameOverride": "override", - }, - expectedValues: map[string]interface{}{ - "nameOverride": "override", - "imageVersion": "0.1.2", - }, - }, - { - name: "nested override", - releaseValues: map[string]interface{}{ - "name": map[string]interface{}{ - "override": "", - }, - "imageVersion": "0.1.2", - }, - renderedValues: map[string]interface{}{ - "name": map[string]interface{}{ - "override": "override", - }, - }, - expectedValues: map[string]interface{}{ - "name": map[string]interface{}{ - "override": "override", - }, - "imageVersion": "0.1.2", - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - mergedValues, err := GetMergedValues(test.releaseValues, test.renderedValues) - if !reflect.DeepEqual(mergedValues, test.expectedValues) { - t.Errorf("GetMergedValues() = %v, want %v", mergedValues, test.expectedValues) - } - if err != nil { - t.Errorf("GetMergedValues() threw an error = %v", err) - } - }) - } -} - -func Test_RenderValuesFromConfig(t *testing.T) { - app := &apptypes.HelmApp{ - Version: 1, - Release: helmrelease.Release{ - Name: "testapp", - }, - } - - license := &kotsv1beta1.License{ - Spec: kotsv1beta1.LicenseSpec{}, - } - - tests := []struct { - name string - app *apptypes.HelmApp - newConfigItems *kotsv1beta1.ConfigValues - chart []byte - expectedValues map[string]interface{} - }{ - { - name: "top level override", - app: app, - newConfigItems: &kotsv1beta1.ConfigValues{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigValues", - APIVersion: "kots.io/v1beta1", - }, - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "myhelmvalue": { - Value: "myValue", - }, - }, - }, - }, - chart: []byte(`apiVersion: kots.io/v1beta1 -kind: HelmChart -metadata: - name: test-chart -spec: - chart: - name: test-chart - chartVersion: 0.3.17 - helmVersion: v3 - useHelmInstall: true - values: - myHelmValue: repl{{ConfigOption "myhelmvalue"}} -builder: {}`), - expectedValues: map[string]interface{}{ - "myHelmValue": "myValue", - }, - }, - { - name: "nested override", - app: app, - newConfigItems: &kotsv1beta1.ConfigValues{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigValues", - APIVersion: "kots.io/v1beta1", - }, - Spec: kotsv1beta1.ConfigValuesSpec{ - Values: map[string]kotsv1beta1.ConfigValue{ - "myhelmvalue": { - Value: "myValue", - }, - }, - }, - }, - chart: []byte(`apiVersion: kots.io/v1beta1 -kind: HelmChart -metadata: - name: test-chart -spec: - chart: - name: test-chart - chartVersion: 0.3.17 - helmVersion: v3 - useHelmInstall: true - values: - myHelmValue: - toOverride: repl{{ConfigOption "myhelmvalue"}} -builder: {}`), - expectedValues: map[string]interface{}{ - "myHelmValue": map[string]interface{}{ - "toOverride": "myValue", - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - kotsKinds := kotsutil.EmptyKotsKinds() - kotsKinds.License = license - kotsKinds.ConfigValues = test.newConfigItems - - renderedValues, err := RenderValuesFromConfig(test.app, &kotsKinds, test.chart) - if !reflect.DeepEqual(renderedValues, test.expectedValues) { - t.Errorf("RenderValuesFromConfig() = %v, want %v", renderedValues, test.expectedValues) - } - if err != nil { - t.Errorf("RenderValuesFromConfig() threw an error = %v", err) - } - }) - } -} diff --git a/pkg/helm/informers.go b/pkg/helm/informers.go deleted file mode 100644 index c8a3044b4a..0000000000 --- a/pkg/helm/informers.go +++ /dev/null @@ -1,181 +0,0 @@ -package helm - -import ( - "encoding/json" - "fmt" - "sync" - "time" - - "github.com/mitchellh/hashstructure" - "github.com/pkg/errors" - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/appstate" - appstatetypes "github.com/replicatedhq/kots/pkg/appstate/types" - identitydeploy "github.com/replicatedhq/kots/pkg/identity/deploy" - identitytypes "github.com/replicatedhq/kots/pkg/identity/types" - "github.com/replicatedhq/kots/pkg/kotsutil" - "github.com/replicatedhq/kots/pkg/logger" - registrytypes "github.com/replicatedhq/kots/pkg/registry/types" - "github.com/replicatedhq/kots/pkg/render" - "github.com/replicatedhq/kots/pkg/template" - "github.com/replicatedhq/kots/pkg/util" - "k8s.io/client-go/kubernetes" -) - -var monitorMap map[string]*appstate.Monitor -var monitorMux *sync.Mutex - -func initMonitor(clientset kubernetes.Interface, targetNamespace string) { - if monitorMap == nil { - monitorMap = make(map[string]*appstate.Monitor) - monitorMux = new(sync.Mutex) - } - - monitorMux.Lock() - if monitorMap[targetNamespace] == nil { - monitorMap[targetNamespace] = appstate.NewMonitor(clientset, targetNamespace) - } - nsMon := monitorMap[targetNamespace] - monitorMux.Unlock() - - go runAppStateMonitor(nsMon) -} - -func getMonitor(namespace string) *appstate.Monitor { - monitorMux.Lock() - defer monitorMux.Unlock() - return monitorMap[namespace] -} - -func resumeHelmStatusInformers(appName string) { - helmApp := GetHelmApp(appName) - app := &apptypes.App{ - ID: helmApp.GetID(), - Slug: helmApp.GetSlug(), - Name: helmApp.Release.Name, - IsAirgap: helmApp.GetIsAirgap(), - CurrentSequence: helmApp.GetCurrentSequence(), - UpdatedAt: &helmApp.CreationTimestamp, - CreatedAt: helmApp.CreationTimestamp, - LastUpdateCheckAt: &helmApp.CreationTimestamp, - IsConfigurable: helmApp.IsConfigurable, - } - - kotsKinds, err := GetKotsKindsFromHelmApp(helmApp) - if err != nil { - logger.Errorf("failed to get kots kinds from helm app: %v\n", err) - return - } - - settings := registrytypes.RegistrySettings{ - Namespace: util.PodNamespace, - } - builder, err := render.NewBuilder(&kotsKinds, settings, app.Slug, helmApp.GetCurrentSequence(), app.IsAirgap, util.PodNamespace) - if err != nil { - logger.Errorf("failed to create new builder: %v\n", err) - return - } - - // apply the informers for this app since they havent been applied yet - if err := applyStatusInformers(app, helmApp.GetCurrentSequence(), &kotsKinds, builder, helmApp.Namespace); err != nil { - logger.Errorf("failed to apply status informers: %v\n", err) - return - } -} - -func applyStatusInformers(a *apptypes.App, sequence int64, kotsKinds *kotsutil.KotsKinds, builder *template.Builder, namespace string) error { - renderedInformers := []appstatetypes.StatusInformerString{} - - // render status informers - for _, informer := range kotsKinds.KotsApplication.Spec.StatusInformers { - renderedInformer, err := builder.String(informer) - if err != nil { - logger.Errorf("failed to render status informer: %v\n", err) - continue - } - if renderedInformer == "" { - continue - } - renderedInformers = append(renderedInformers, appstatetypes.StatusInformerString(renderedInformer)) - } - - if identitydeploy.IsEnabled(kotsKinds.Identity, kotsKinds.IdentityConfig) { - renderedInformers = append(renderedInformers, appstatetypes.StatusInformerString(fmt.Sprintf("deployment/%s", identitytypes.DeploymentName(a.Slug)))) - } - - if len(renderedInformers) > 0 { - applyAppInformers(a.ID, sequence, renderedInformers, namespace) - } else { - // no informers, set state to ready - defaultReadyState := appstatetypes.ResourceStates{ - { - Kind: "EMPTY", - Name: "EMPTY", - Namespace: "EMPTY", - State: appstatetypes.StateReady, - }, - } - - release := GetHelmApp(a.ID) - appStatus := appstatetypes.AppStatus{ - AppID: a.ID, - ResourceStates: defaultReadyState, - Sequence: release.GetCurrentSequence(), - UpdatedAt: time.Now(), - } - release.Status = appStatus - release.Status.State = appstatetypes.GetState(defaultReadyState) - } - - return nil -} - -func applyAppInformers(appID string, sequence int64, informerStrings []appstatetypes.StatusInformerString, namespace string) { - logger.Infof("received an inform event for appID (%s) sequence (%d) with informerStrings (%+v)\n", appID, sequence, informerStrings) - - var informers []appstatetypes.StatusInformer - for _, str := range informerStrings { - informer, err := str.Parse() - if err != nil { - logger.Infof("failed to parse informer %s: %s", str, err.Error()) - continue // don't stop - } - informers = append(informers, informer) - } - if len(informers) > 0 { - if mon := getMonitor(namespace); mon != nil { - mon.Apply(appID, sequence, informers) - } - } -} - -func runAppStateMonitor(monitor *appstate.Monitor) error { - m := map[string]func(f func()){} - hash := map[string]uint64{} - var mtx sync.Mutex - - for appStatus := range monitor.AppStatusChan() { - throttled, ok := m[appStatus.AppID] - if !ok { - throttled = util.NewThrottle(time.Second) - m[appStatus.AppID] = throttled - } - throttled(func() { - mtx.Lock() - lastHash := hash[appStatus.AppID] - nextHash, _ := hashstructure.Hash(appStatus, nil) - hash[appStatus.AppID] = nextHash - mtx.Unlock() - if lastHash != nextHash { - b, _ := json.Marshal(appStatus) - logger.Infof("Updating app status %s", b) - } - - app := GetHelmApp(appStatus.AppID) - app.Status = appStatus - app.Status.State = appstatetypes.GetState(appStatus.ResourceStates) - }) - } - - return errors.New("app state monitor shutdown") -} diff --git a/pkg/helm/license.go b/pkg/helm/license.go deleted file mode 100644 index 1a1c83e44c..0000000000 --- a/pkg/helm/license.go +++ /dev/null @@ -1,46 +0,0 @@ -package helm - -import ( - "github.com/pkg/errors" - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/replicatedapp" -) - -func SyncLicense(helmApp *apptypes.HelmApp) (bool, error) { - isSynced := false - - currentLicense, err := GetChartLicenseFromSecretOrDownload(helmApp) - if err != nil { - return isSynced, errors.Wrap(err, "failed to get license from secret") - } - - licenseID := GetKotsLicenseID(&helmApp.Release) - - if currentLicense == nil && licenseID == "" { - return isSynced, errors.Errorf("no license found for release %s", helmApp.Release.Name) - } - - if licenseID == "" { - licenseID = currentLicense.Spec.LicenseID - } else if currentLicense != nil && licenseID != currentLicense.Spec.LicenseID { - return isSynced, errors.Errorf("license ID in the chart does not match license ID in secret for release %s", helmApp.Release.Name) - } - - latestLicenseData, err := replicatedapp.GetLatestLicenseForHelm(licenseID) - if err != nil { - return isSynced, errors.Wrap(err, "failed to get latest license for helm app") - } - - err = SaveChartLicenseInSecret(helmApp, latestLicenseData.LicenseBytes) - if err != nil { - return isSynced, errors.Wrap(err, "failed to update helm license") - } - - if currentLicense == nil { - isSynced = true - } else if currentLicense.Spec.LicenseSequence != latestLicenseData.License.Spec.LicenseSequence { - isSynced = true - } - - return isSynced, nil -} diff --git a/pkg/helm/update_release.go b/pkg/helm/update_release.go index 3877edcda2..2887cddebd 100644 --- a/pkg/helm/update_release.go +++ b/pkg/helm/update_release.go @@ -6,9 +6,11 @@ import ( "context" "encoding/base64" "encoding/json" + "io" "github.com/pkg/errors" "helm.sh/helm/v3/pkg/release" + helmrelease "helm.sh/helm/v3/pkg/release" corev1 "k8s.io/api/core/v1" kuberneteserrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -58,7 +60,7 @@ func MigrateExistingHelmReleaseSecrets(clientset kubernetes.Interface, releaseNa // moveHelmReleaseSecret will create a new secret in the releaseNamespace and delete the old one from the kotsadmNamespace func moveHelmReleaseSecret(clientset kubernetes.Interface, secret corev1.Secret, releaseNamespace string, kotsadmNamespace string) error { - release, err := HelmReleaseFromSecretData(secret.Data["release"]) + release, err := helmReleaseFromSecretData(secret.Data["release"]) if err != nil { return errors.Wrapf(err, "failed to get release from secret data") } @@ -105,3 +107,25 @@ func encodeRelease(helmRelease *release.Release) (string, error) { return base64.StdEncoding.EncodeToString(buf.Bytes()), nil } + +func helmReleaseFromSecretData(data []byte) (*helmrelease.Release, error) { + base64Reader := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(data)) + gzreader, err := gzip.NewReader(base64Reader) + if err != nil { + return nil, errors.Wrap(err, "failed to create gzip reader") + } + defer gzreader.Close() + + releaseData, err := io.ReadAll(gzreader) + if err != nil { + return nil, errors.Wrap(err, "failed to read from gzip reader") + } + + release := &helmrelease.Release{} + err = json.Unmarshal(releaseData, &release) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal release data") + } + + return release, nil +} diff --git a/pkg/helm/update_release_test.go b/pkg/helm/update_release_test.go index c1fa9a281c..87d8d23337 100644 --- a/pkg/helm/update_release_test.go +++ b/pkg/helm/update_release_test.go @@ -141,7 +141,7 @@ func TestMigrateExistingHelmReleaseSecrets(t *testing.T) { t.Errorf("expected helm release secret to be in namespace %s, but was in %s", tt.args.releaseNamespace, secret.Namespace) } - release, err := HelmReleaseFromSecretData(secret.Data["release"]) + release, err := helmReleaseFromSecretData(secret.Data["release"]) if err != nil { t.Errorf("failed to get helm release from secret data: %v", err) } diff --git a/pkg/helm/updates.go b/pkg/helm/updates.go deleted file mode 100644 index 6fdb57b682..0000000000 --- a/pkg/helm/updates.go +++ /dev/null @@ -1,455 +0,0 @@ -package helm - -import ( - "bytes" - "context" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sort" - "strings" - "sync" - "time" - - "github.com/blang/semver" - "github.com/containers/image/v5/docker" - imagetypes "github.com/containers/image/v5/types" - "github.com/pkg/errors" - apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/k8sutil" - kotsadmtypes "github.com/replicatedhq/kots/pkg/kotsadm/types" - "github.com/replicatedhq/kots/pkg/kotsutil" - "github.com/replicatedhq/kots/pkg/logger" - storetypes "github.com/replicatedhq/kots/pkg/store/types" - "github.com/replicatedhq/kots/pkg/util" - kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" - "go.uber.org/zap" - "gopkg.in/yaml.v3" - helmgetter "helm.sh/helm/v3/pkg/getter" - corev1 "k8s.io/api/core/v1" - kuberneteserrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/scheme" -) - -type ChartUpdate struct { - Tag string - Version semver.Version - Status storetypes.DownstreamVersionStatus - CreatedOn *time.Time - IsDownloaded bool -} - -type ReplicatedMeta struct { - LicenseID string `yaml:"license_id"` - Username string `yaml:"username"` - CreatedAt *time.Time `yaml:"created_at"` - // TODO "app": map[string][]byte -} - -type ChartUpdates []ChartUpdate - -var ( - updateCacheMutex sync.Mutex - updateCache map[string]ChartUpdates // available updates sorted in descending order for each chart -) - -func init() { - updateCache = make(map[string]ChartUpdates) -} - -func (v ChartUpdates) Len() int { - return len(v) -} - -func (v ChartUpdates) Swap(i, j int) { - v[i], v[j] = v[j], v[i] -} - -func (v ChartUpdates) Less(i, j int) bool { - return v[i].Version.LT(v[j].Version) -} - -func (u ChartUpdates) ToTagList() []string { - tags := []string{} - for _, update := range u { - tags = append(tags, update.Tag) - } - return tags -} - -func GetCachedUpdates(chartPath string) ChartUpdates { - updateCacheMutex.Lock() - defer updateCacheMutex.Unlock() - - return updateCache[chartPath] -} - -func GetDownloadedUpdates(chartPath string) ChartUpdates { - updateCacheMutex.Lock() - defer updateCacheMutex.Unlock() - - downloadedUpdates := ChartUpdates{} - for _, u := range updateCache[chartPath] { - if u.IsDownloaded { - downloadedUpdates = append(downloadedUpdates, u) - } - } - return downloadedUpdates -} - -func SetCachedUpdateStatus(chartPath string, tag string, status storetypes.DownstreamVersionStatus) { - updates := GetCachedUpdates(chartPath) - for i, u := range updates { - if u.Tag == tag { - updates[i].Status = status - break - } - } -} - -func SetCachedUpdateMetadata(chartPath string, tag string, meta *ReplicatedMeta) { - updates := GetCachedUpdates(chartPath) - for i, u := range updates { - if u.Tag == tag { - updates[i].IsDownloaded = true // Metadata is only known when version is downloaded - if meta != nil { - updates[i].CreatedOn = meta.CreatedAt - } - break - } - } -} - -func setCachedUpdates(chartPath string, updates ChartUpdates) { - updateCacheMutex.Lock() - defer updateCacheMutex.Unlock() - - updateCache[chartPath] = updates -} - -// Removes this tag from cache and also every tag that is less than this one according to semver ordering -func removeFromCachedUpdates(chartPath string, tag string) { - updateCacheMutex.Lock() - defer updateCacheMutex.Unlock() - - version, parseErr := semver.ParseTolerant(tag) - - existingList := updateCache[chartPath] - newList := ChartUpdates{} - for _, update := range existingList { - // If tag cannot be parsed, fall back on string comparison. - // This should never happen for versions that are on the list because we only include valid semvers and Helm chart versions are valid semvers. - if parseErr != nil { - if update.Tag != tag { - newList = append(newList, update) - } - } else if update.Version.GT(version) { - newList = append(newList, update) - } - } - updateCache[chartPath] = newList -} - -func deleteUpdateCacheForChart(chartPath string) { - updateCacheMutex.Lock() - defer updateCacheMutex.Unlock() - - delete(updateCache, chartPath) -} - -func CheckForUpdates(helmApp *apptypes.HelmApp, license *kotsv1beta1.License, currentVersion *semver.Version) (ChartUpdates, error) { - chartPath := helmApp.ChartPath - licenseID := license.Spec.LicenseID - - _, err := SyncLicense(helmApp) - if err != nil { - return nil, errors.Wrapf(err, "failed to sync license before update check") - } - - imageName := strings.TrimLeft(chartPath, "oci:") - ref, err := docker.ParseReference(imageName) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse image ref %q", imageName) - } - - sysCtx := &imagetypes.SystemContext{ - DockerInsecureSkipTLSVerify: imagetypes.OptionalBoolTrue, - DockerDisableV1Ping: true, - DockerAuthConfig: &imagetypes.DockerAuthConfig{ - Username: licenseID, - Password: licenseID, - }, - } - - tags, err := docker.GetRepositoryTags(context.TODO(), sysCtx, ref) - if err != nil { - return nil, errors.Wrapf(err, "failed to get repo tags") - } - - tags = removeDuplicates(tags) // registry should not be returning duplicate tags - - availableUpdates := ChartUpdates{} - for _, tag := range tags { - v, err := semver.ParseTolerant(tag) - if err != nil { - // TODO: log - continue - } - - if currentVersion != nil && v.LE(*currentVersion) { - continue - } - - availableUpdates = append(availableUpdates, ChartUpdate{ - Tag: tag, - Version: v, - IsDownloaded: false, - }) - } - - sort.Sort(sort.Reverse(ChartUpdates(availableUpdates))) - - setCachedUpdates(chartPath, availableUpdates) - - return availableUpdates, nil -} - -func removeDuplicates(tags []string) []string { - m := map[string]struct{}{} - for _, tag := range tags { - m[tag] = struct{}{} - } - - u := []string{} - for k := range m { - u = append(u, k) - } - - return u -} - -func GetKotsKindsFromUpstreamChartVersion(helmApp *apptypes.HelmApp, licenseID string, version string) (kotsutil.KotsKinds, error) { - secret, err := GetReplicatedSecretFromUpstreamChartVersion(helmApp, licenseID, version) - if err != nil { - return kotsutil.KotsKinds{}, errors.Wrap(err, "failed to get secret upstream archive") - } - - kotsKinds, err := GetKotsKindsFromReplicatedSecret(secret) - if err != nil { - return kotsKinds, errors.Wrap(err, "failed to get kots kinds from secret") - } - - return kotsKinds, nil -} - -func GetReplicatedSecretFromUpstreamChartVersion(helmApp *apptypes.HelmApp, licenseID string, version string) (*corev1.Secret, error) { - chartData, err := downloadChartReleaseIfNeeded(helmApp, licenseID, version) - if err != nil { - return nil, errors.Wrap(err, "failed to get chart data") - } - - templatedData, err := util.GetFileFromTGZArchive(chartData, "**/templates/_replicated/secret.yaml") - if err != nil { - return nil, errors.Wrap(err, "failed to get secret file from chart archive") - } - - secretData, err := removeHelmTemplate(templatedData.Bytes()) - if err != nil { - return nil, errors.Wrap(err, "failed to remove helm templates from replicated secret file") - } - - decode := scheme.Codecs.UniversalDeserializer().Decode - obj, gvk, err := decode(secretData, nil, nil) - if err != nil { - return nil, errors.Wrap(err, "failed to decode secret data") - } - - if gvk.Group != "" || gvk.Version != "v1" || gvk.Kind != "Secret" { - return nil, errors.Errorf("unexpected secret GVK: %s", gvk.String()) - } - - return obj.(*corev1.Secret), nil -} - -func GetReplicatedMetadataFromUpstreamChartVersion(helmApp *apptypes.HelmApp, licenseID string, version string) (*ReplicatedMeta, error) { - chartData, err := downloadChartReleaseIfNeeded(helmApp, licenseID, version) - if err != nil { - return nil, errors.Wrap(err, "failed to get chart data") - } - - valuesData, err := util.GetFileFromTGZArchive(chartData, "**/values.yaml") - if err != nil { - return nil, errors.Wrap(err, "failed to get values.yaml from chart archive") - } - - b := valuesData.Bytes() - values := struct { - Replicated *ReplicatedMeta `yaml:"replicated,omitempty"` - }{} - err = yaml.Unmarshal(b, &values) - if err != nil { - return nil, errors.Wrap(err, "failed to decode values.yaml") - } - - return values.Replicated, nil -} - -func downloadChartReleaseIfNeeded(helmApp *apptypes.HelmApp, licenseID string, version string) (*bytes.Buffer, error) { - chartData, err := getUpdateChartFromCache(helmApp, version) - if err != nil { - return nil, errors.Wrap(err, "failed to get release from cache") - } - - if chartData != nil { - return chartData, nil - } - - err = CreateHelmRegistryCreds(licenseID, licenseID, helmApp.ChartPath) - if err != nil { - return nil, errors.Wrap(err, "failed to create helm credentials file") - } - chartGetter, err := helmgetter.NewOCIGetter() - if err != nil { - return nil, errors.Wrap(err, "failed to create chart getter") - } - - imageName := fmt.Sprintf("%s:%s", helmApp.ChartPath, version) - chartData, err = chartGetter.Get(imageName) - if err != nil { - return nil, errors.Wrapf(err, "failed to get chart %q", imageName) - } - - chartData, err = saveUpdateChartInCache(helmApp, version, chartData) - if err != nil { - logger.Info("failed to save chart in cache", zap.String("error", err.Error())) - } - - return chartData, nil -} - -var ( - updateCacheDir = "" -) - -func getUpdateChartFromCache(helmApp *apptypes.HelmApp, version string) (*bytes.Buffer, error) { - fileName := getUpdateChacheFileName(helmApp, version) - b, err := os.ReadFile(fileName) - if err != nil { - if os.IsNotExist(err) { - return nil, nil - } - return nil, errors.Wrap(err, "failed to read file") - } - - return bytes.NewBuffer(b), nil -} - -func saveUpdateChartInCache(helmApp *apptypes.HelmApp, version string, data *bytes.Buffer) (*bytes.Buffer, error) { - b := data.Bytes() - newBuff := bytes.NewBuffer(b) - - if updateCacheDir == "" { - dirName, err := ioutil.TempDir("", "chart-updates-") - if err != nil { - return newBuff, errors.Wrap(err, "failed to create temp dir") - } - updateCacheDir = dirName - } - - fileName := getUpdateChacheFileName(helmApp, version) - - err := os.MkdirAll(filepath.Dir(fileName), 0755) - if err != nil { - return newBuff, errors.Wrap(err, "failed to create cache dir") - } - - err = ioutil.WriteFile(fileName, b, 0744) - if err != nil { - return newBuff, errors.Wrap(err, "failed to save cache file") - } - - return newBuff, nil -} - -func getUpdateChacheFileName(helmApp *apptypes.HelmApp, version string) string { - return filepath.Join(updateCacheDir, strings.TrimPrefix(helmApp.ChartPath, "oci://"), fmt.Sprintf("%s.tgz", version)) -} - -func GetUpdateCheckSpec(helmApp *apptypes.HelmApp) (string, error) { - clientset, err := k8sutil.GetClientset() - if err != nil { - return "", errors.Wrap(err, "failed to get clientset") - } - - spec := "@default" - - cm, err := clientset.CoreV1().ConfigMaps(util.PodNamespace).Get(context.TODO(), kotsadmtypes.KotsadmConfigMap, metav1.GetOptions{}) - if err != nil && !kuberneteserrors.IsNotFound(err) { - return "", errors.Wrap(err, "failed to get configmap") - } else if kuberneteserrors.IsNotFound(err) { - return spec, nil - } - - if cm.Data == nil { - return spec, nil - } - - key := fmt.Sprintf("update-schedule-%s", helmApp.GetID()) - if s := cm.Data[key]; s != "" { - spec = s - } - - return spec, nil -} - -var configMapMutex sync.Mutex - -func SetUpdateCheckSpec(helmApp *apptypes.HelmApp, updateSpec string) error { - configMapMutex.Lock() - defer configMapMutex.Unlock() - - clientset, err := k8sutil.GetClientset() - if err != nil { - return errors.Wrap(err, "failed to get clientset") - } - - key := fmt.Sprintf("update-schedule-%s", helmApp.GetID()) - - cm, err := clientset.CoreV1().ConfigMaps(util.PodNamespace).Get(context.TODO(), kotsadmtypes.KotsadmConfigMap, metav1.GetOptions{}) - if err != nil && !kuberneteserrors.IsNotFound(err) { - return errors.Wrap(err, "failed to get configmap") - } else if kuberneteserrors.IsNotFound(err) { - cm := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ConfigMap", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: kotsadmtypes.KotsadmConfigMap, - Namespace: util.PodNamespace, - Labels: kotsadmtypes.GetKotsadmLabels(), - }, - Data: map[string]string{ - key: updateSpec, - }, - } - _, err = clientset.CoreV1().ConfigMaps(util.PodNamespace).Create(context.Background(), cm, metav1.CreateOptions{}) - if err != nil { - return errors.Wrap(err, "failed to update config map") - } - } - - if cm.Data == nil { - cm.Data = map[string]string{} - } - cm.Data[key] = updateSpec - - _, err = clientset.CoreV1().ConfigMaps(util.PodNamespace).Update(context.Background(), cm, metav1.UpdateOptions{}) - if err != nil { - return errors.Wrap(err, "failed to update config map") - } - - return nil -} diff --git a/pkg/policy/getters.go b/pkg/policy/getters.go index 58e337b01b..3cb1d7dbbd 100644 --- a/pkg/policy/getters.go +++ b/pkg/policy/getters.go @@ -3,7 +3,6 @@ package policy import ( "github.com/pkg/errors" "github.com/replicatedhq/kots/pkg/store" - "github.com/replicatedhq/kots/pkg/util" ) func appSlugFromAppIDGetter(kotsStore store.Store, vars map[string]string) (map[string]string, error) { @@ -15,19 +14,13 @@ func appSlugFromAppIDGetter(kotsStore store.Store, vars map[string]string) (map[ return map[string]string{}, nil } - var appSlug string - if util.IsHelmManaged() { - appSlug = appIDOrSlug - } else { - app, err := kotsStore.GetApp(appIDOrSlug) - if err != nil { - return nil, errors.Wrap(err, "failed to get app") - } - appSlug = app.Slug + app, err := kotsStore.GetApp(appIDOrSlug) + if err != nil { + return nil, errors.Wrap(err, "failed to get app") } return map[string]string{ - "appSlug": appSlug, + "appSlug": app.Slug, }, nil } @@ -52,17 +45,12 @@ func appSlugFromSupportbundleGetter(kotsStore store.Store, vars map[string]strin return nil, nil } - var appSlug string - if util.IsHelmManaged() { - appSlug = appIDOrSlug - } else { - app, err := kotsStore.GetApp(appIDOrSlug) - if err != nil { - return nil, errors.Wrap(err, "failed to get app") - } - appSlug = app.Slug + app, err := kotsStore.GetApp(appIDOrSlug) + if err != nil { + return nil, errors.Wrap(err, "failed to get app") } + return map[string]string{ - "appSlug": appSlug, + "appSlug": app.Slug, }, nil } diff --git a/pkg/policy/policies.go b/pkg/policy/policies.go index 1848d666e0..dd6d360596 100644 --- a/pkg/policy/policies.go +++ b/pkg/policy/policies.go @@ -152,10 +152,3 @@ var ( AppDownstreamConfigRead = Must(NewPolicy(ActionRead, "app.{{.appSlug}}.downstream.config.")) AppDownstreamConfigWrite = Must(NewPolicy(ActionWrite, "app.{{.appSlug}}.downstream.config.")) ) - -// Helm - -var ( - IsHelmManaged = Must(NewPolicy(ActionRead, "")) - GetAppValuesFile = Must(NewPolicy(ActionRead, "")) -) diff --git a/pkg/redact/app.go b/pkg/redact/app.go index 49e8a02c8b..c7445b02ef 100644 --- a/pkg/redact/app.go +++ b/pkg/redact/app.go @@ -29,7 +29,7 @@ func GetAppRedactSpecURI(appSlug string) string { } // CreateRenderedAppRedactSpec creates a configmap that contains the redaction yaml spec included in the application release -func CreateRenderedAppRedactSpec(clientset kubernetes.Interface, app apptypes.AppType, sequence int64, kotsKinds *kotsutil.KotsKinds) error { +func CreateRenderedAppRedactSpec(clientset kubernetes.Interface, app *apptypes.App, sequence int64, kotsKinds *kotsutil.KotsKinds) error { builtRedactor := kotsKinds.Redactor.DeepCopy() if builtRedactor == nil { builtRedactor = &troubleshootv1beta2.Redactor{ diff --git a/pkg/render/helper/appfile.go b/pkg/render/helper/appfile.go index c8bb94b37a..74313e32df 100644 --- a/pkg/render/helper/appfile.go +++ b/pkg/render/helper/appfile.go @@ -4,16 +4,14 @@ import ( "github.com/pkg/errors" "github.com/replicatedhq/kots/pkg/app/types" "github.com/replicatedhq/kots/pkg/kotsutil" - registrytypes "github.com/replicatedhq/kots/pkg/registry/types" "github.com/replicatedhq/kots/pkg/render" rendertypes "github.com/replicatedhq/kots/pkg/render/types" "github.com/replicatedhq/kots/pkg/store" - "github.com/replicatedhq/kots/pkg/util" ) // RenderAppFile renders a single file using the current sequence of the provided app, or the overrideSequence (if provided) // it's here for now to avoid an import cycle between kotsadm/pkg/render and pkg/store -func RenderAppFile(a types.AppType, overrideSequence *int64, inputContent []byte, kotsKinds *kotsutil.KotsKinds, namespace string) ([]byte, error) { +func RenderAppFile(a *types.App, overrideSequence *int64, inputContent []byte, kotsKinds *kotsutil.KotsKinds, namespace string) ([]byte, error) { var sequence int64 if overrideSequence != nil { sequence = *overrideSequence @@ -25,13 +23,9 @@ func RenderAppFile(a types.AppType, overrideSequence *int64, inputContent []byte sequence = latestSequence } - var registrySettings registrytypes.RegistrySettings - if !util.IsHelmManaged() { - s, err := store.GetStore().GetRegistryDetailsForApp(a.GetID()) - if err != nil { - return nil, errors.Wrap(err, "failed to load registry settings") - } - registrySettings = s + registrySettings, err := store.GetStore().GetRegistryDetailsForApp(a.GetID()) + if err != nil { + return nil, errors.Wrap(err, "failed to load registry settings") } return render.RenderFile(rendertypes.RenderFileOptions{ diff --git a/pkg/replicatedapp/api.go b/pkg/replicatedapp/api.go index 9286617bd2..5d1863e3a8 100644 --- a/pkg/replicatedapp/api.go +++ b/pkg/replicatedapp/api.go @@ -51,16 +51,6 @@ func GetLatestLicense(license *kotsv1beta1.License) (*LicenseData, error) { return licenseData, nil } -func GetLatestLicenseForHelm(licenseID string) (*LicenseData, error) { - url := fmt.Sprintf("%s/license", util.GetReplicatedAPIEndpoint()) - licenseData, err := getLicenseFromAPI(url, licenseID) - if err != nil { - return nil, errors.Wrap(err, "failed to get helm license from api") - } - - return licenseData, nil -} - func getAppIdFromLicenseId(s store.Store, licenseID string) (string, error) { apps, err := s.ListInstalledApps() if err != nil { diff --git a/pkg/reporting/app.go b/pkg/reporting/app.go index 24feb8a4ea..f70cbd6441 100644 --- a/pkg/reporting/app.go +++ b/pkg/reporting/app.go @@ -1,10 +1,7 @@ package reporting import ( - "bytes" - "compress/gzip" "context" - "encoding/base64" "encoding/json" "fmt" "io/ioutil" @@ -27,16 +24,9 @@ import ( "github.com/segmentio/ksuid" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" veleroclientv1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - helmrelease "helm.sh/helm/v3/pkg/release" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" ) -var ( - clusterID string // set when in Helm managed mode -) - type SnapshotReport struct { Provider string FullSchedule string @@ -46,16 +36,9 @@ type SnapshotReport struct { } func Init() error { - if util.IsHelmManaged() { - err := initFromHelm() - if err != nil { - return errors.Wrap(err, "failed to init from Helm") - } - } else { - err := initFromDownstream() - if err != nil { - return errors.Wrap(err, "failed to init from downstream") - } + err := initFromDownstream() + if err != nil { + return errors.Wrap(err, "failed to init from downstream") } if kotsadm.IsAirgap() { @@ -74,69 +57,6 @@ func Init() error { return nil } -func initFromHelm() error { - // ClusterID in reporting will be the UID of the v1 of Admin Console secret - clientSet, err := k8sutil.GetClientset() - if err != nil { - return errors.Wrap(err, "failed to get clientset") - } - - selectorLabels := map[string]string{ - "owner": "helm", - "version": "1", - } - listOpts := metav1.ListOptions{ - LabelSelector: labels.SelectorFromSet(selectorLabels).String(), - } - - secrets, err := clientSet.CoreV1().Secrets(util.PodNamespace).List(context.TODO(), listOpts) - if err != nil { - return errors.Wrap(err, "failed to list secrets") - } - - for _, secret := range secrets.Items { - helmRelease, err := helmReleaseFromSecretData(secret.Data["release"]) - if err != nil { - logger.Warnf("failed to parse helm chart in secret %s: %v", &secret.ObjectMeta.Name, err) - continue - } - - if helmRelease.Chart == nil || helmRelease.Chart.Metadata == nil { - continue - } - if helmRelease.Chart.Metadata.Name != "admin-console" { - continue - } - - clusterID = string(secret.ObjectMeta.UID) - return nil - } - - return errors.New("admin-console secret v1 not found") -} - -func helmReleaseFromSecretData(data []byte) (*helmrelease.Release, error) { - base64Reader := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(data)) - gzreader, err := gzip.NewReader(base64Reader) - if err != nil { - return nil, errors.Wrap(err, "failed to create gzip reader") - } - defer gzreader.Close() - - releaseData, err := ioutil.ReadAll(gzreader) - if err != nil { - return nil, errors.Wrap(err, "failed to read from gzip reader") - } - - release := &helmrelease.Release{} - err = json.Unmarshal(releaseData, &release) - if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal release data") - } - - return release, nil -} - func initFromDownstream() error { // Retrieve the ClusterID from store clusters, err := store.GetStore().ListClusters() @@ -208,19 +128,14 @@ func GetReportingInfo(appID string) *types.ReportingInfo { if err != nil { logger.Debugf("failed to get clientset: %v", err.Error()) } + r.ClusterID = k8sutil.GetKotsadmID(clientset) - if util.IsHelmManaged() { - r.ClusterID = clusterID - } else { - r.ClusterID = k8sutil.GetKotsadmID(clientset) - - di, err := getDownstreamInfo(appID) - if err != nil { - logger.Debugf("failed to get downstream info: %v", err.Error()) - } - if di != nil { - r.Downstream = *di - } + di, err := getDownstreamInfo(appID) + if err != nil { + logger.Debugf("failed to get downstream info: %v", err.Error()) + } + if di != nil { + r.Downstream = *di } // get kubernetes cluster version @@ -236,15 +151,11 @@ func GetReportingInfo(appID string) *types.ReportingInfo { } // get app status - if util.IsHelmManaged() { - logger.Infof("TODO: get app status in Helm managed mode") + appStatus, err := store.GetStore().GetAppStatus(appID) + if err != nil { + logger.Debugf("failed to get app status: %v", err.Error()) } else { - appStatus, err := store.GetStore().GetAppStatus(appID) - if err != nil { - logger.Debugf("failed to get app status: %v", err.Error()) - } else { - r.AppStatus = string(appStatus.State) - } + r.AppStatus = string(appStatus.State) } r.IsKurl, err = kurl.IsKurl(clientset) diff --git a/pkg/store/kotsstore/supportbundle_store.go b/pkg/store/kotsstore/supportbundle_store.go index 71c820597b..a3f4f88000 100644 --- a/pkg/store/kotsstore/supportbundle_store.go +++ b/pkg/store/kotsstore/supportbundle_store.go @@ -32,65 +32,9 @@ import ( ) var ( - // Used in Helm managed mode supportBundleSecretMtx sync.Mutex - supportBundlesByID = map[string]*types.SupportBundle{} - supportBundlesIDsByApp = map[string][]string{} ) -func addSupportBundleToCache(bundle *types.SupportBundle) { - supportBundleSecretMtx.Lock() - defer supportBundleSecretMtx.Unlock() - - _, exist := supportBundlesByID[bundle.ID] - supportBundlesByID[bundle.ID] = bundle - - if exist { - return - } - - _, ok := supportBundlesIDsByApp[bundle.AppID] - if ok { - supportBundlesIDsByApp[bundle.AppID] = append(supportBundlesIDsByApp[bundle.AppID], bundle.ID) - } else { - supportBundlesIDsByApp[bundle.AppID] = []string{bundle.ID} - } -} - -func getSupportBundleFromCache(id string) *types.SupportBundle { - supportBundleSecretMtx.Lock() - defer supportBundleSecretMtx.Unlock() - - bundle := supportBundlesByID[id] - if bundle != nil { - bundle.Status = getSupportBundleStatus(bundle.Status, bundle.UpdatedAt) - } - - return bundle -} - -func getSupportBundleIDsFromCache(appID string) []string { - supportBundleSecretMtx.Lock() - defer supportBundleSecretMtx.Unlock() - - return supportBundlesIDsByApp[appID] -} - -func deleteSupportBundleFromCache(id string, appID string) { - supportBundleSecretMtx.Lock() - defer supportBundleSecretMtx.Unlock() - delete(supportBundlesByID, id) - - oldSupportBundlesIDs := supportBundlesIDsByApp[appID] - newSupportBundlesIDs := []string{} - for _, sbID := range oldSupportBundlesIDs { - if sbID != id { - newSupportBundlesIDs = append(newSupportBundlesIDs, sbID) - } - } - supportBundlesIDsByApp[appID] = newSupportBundlesIDs -} - func (s *KOTSStore) migrateSupportBundlesFromRqlite() error { logger.Debug("migrating support bundles from rqlite") @@ -254,41 +198,31 @@ func (s *KOTSStore) migrateSupportBundlesFromRqlite() error { func (s *KOTSStore) ListSupportBundles(appID string) ([]*types.SupportBundle, error) { supportBundles := []*types.SupportBundle{} - if util.IsHelmManaged() { - ids := getSupportBundleIDsFromCache(appID) - for _, id := range ids { - bundle := getSupportBundleFromCache(id) - if bundle != nil { - supportBundles = append(supportBundles, bundle) - } - } - } else { - clientset, err := k8sutil.GetClientset() - if err != nil { - return nil, errors.Wrap(err, "failed to get clientset") - } + clientset, err := k8sutil.GetClientset() + if err != nil { + return nil, errors.Wrap(err, "failed to get clientset") + } - labelSelector := metav1.LabelSelector{ - MatchLabels: map[string]string{ - "kots.io/kind": "supportbundle", - "kots.io/appid": appID, - }, - } + labelSelector := metav1.LabelSelector{ + MatchLabels: map[string]string{ + "kots.io/kind": "supportbundle", + "kots.io/appid": appID, + }, + } - secrets, err := clientset.CoreV1().Secrets(util.PodNamespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: labels.Set(labelSelector.MatchLabels).String(), - }) - if err != nil { - return nil, errors.Wrap(err, "failed to list support bundles") - } + secrets, err := clientset.CoreV1().Secrets(util.PodNamespace).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labels.Set(labelSelector.MatchLabels).String(), + }) + if err != nil { + return nil, errors.Wrap(err, "failed to list support bundles") + } - for _, secret := range secrets.Items { - supportBundle, err := s.getSupportBundleFromSecretData(&secret) - if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal support bundle") - } - supportBundles = append(supportBundles, supportBundle) + for _, secret := range secrets.Items { + supportBundle, err := s.getSupportBundleFromSecretData(&secret) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal support bundle") } + supportBundles = append(supportBundles, supportBundle) } // sort the bundles here by date, since we don't have a sort order otherwise @@ -299,23 +233,20 @@ func (s *KOTSStore) ListSupportBundles(appID string) ([]*types.SupportBundle, er func (s *KOTSStore) GetSupportBundle(id string) (*types.SupportBundle, error) { supportBundle := &types.SupportBundle{} - if util.IsHelmManaged() { - supportBundle = getSupportBundleFromCache(id) - } else { - clientset, err := k8sutil.GetClientset() - if err != nil { - return nil, errors.Wrap(err, "failed to get clientset") - } - secret, err := clientset.CoreV1().Secrets(util.PodNamespace).Get(context.TODO(), fmt.Sprintf("supportbundle-%s", id), metav1.GetOptions{}) - if err != nil { - return nil, errors.Wrap(err, "failed to get secret") - } + clientset, err := k8sutil.GetClientset() + if err != nil { + return nil, errors.Wrap(err, "failed to get clientset") + } - supportBundle, err = s.getSupportBundleFromSecretData(secret) - if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal") - } + secret, err := clientset.CoreV1().Secrets(util.PodNamespace).Get(context.TODO(), fmt.Sprintf("supportbundle-%s", id), metav1.GetOptions{}) + if err != nil { + return nil, errors.Wrap(err, "failed to get secret") + } + + supportBundle, err = s.getSupportBundleFromSecretData(secret) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal") } if supportBundle != nil { @@ -333,25 +264,20 @@ func (s *KOTSStore) GetSupportBundle(id string) (*types.SupportBundle, error) { } func (s *KOTSStore) DeleteSupportBundle(bundleID string, appID string) error { - if util.IsHelmManaged() { - // delete from cache - deleteSupportBundleFromCache(bundleID, appID) - } else { - clientset, err := k8sutil.GetClientset() - if err != nil { - return errors.Wrap(err, "failed to get clientset") - } + clientset, err := k8sutil.GetClientset() + if err != nil { + return errors.Wrap(err, "failed to get clientset") + } - // delete the secret - if err := clientset.CoreV1().Secrets(util.PodNamespace).Delete(context.TODO(), fmt.Sprintf("supportbundle-%s", bundleID), metav1.DeleteOptions{}); err != nil && !s.IsNotFound(err) { - return errors.Wrap(err, "failed to delete secret") - } + // delete the secret + if err := clientset.CoreV1().Secrets(util.PodNamespace).Delete(context.TODO(), fmt.Sprintf("supportbundle-%s", bundleID), metav1.DeleteOptions{}); err != nil && !s.IsNotFound(err) { + return errors.Wrap(err, "failed to delete secret") + } - // delete the archive - sbPath := filepath.Join("supportbundles", bundleID) - if err := filestore.GetStore().DeleteArchive(sbPath); err != nil { - return errors.Wrap(err, "failed to delete archive") - } + // delete the archive + sbPath := filepath.Join("supportbundles", bundleID) + if err := filestore.GetStore().DeleteArchive(sbPath); err != nil { + return errors.Wrap(err, "failed to delete archive") } return nil } @@ -365,11 +291,6 @@ func (s *KOTSStore) CreateInProgressSupportBundle(supportBundle *types.SupportBu supportBundle.CreatedAt = now supportBundle.UpdatedAt = &now - if util.IsHelmManaged() { - addSupportBundleToCache(supportBundle) - return nil - } - bundleMarshaled, err := json.Marshal(supportBundle) if err != nil { return errors.Wrap(err, "failed to marshal support bundle") @@ -441,11 +362,6 @@ func (s *KOTSStore) CreateSupportBundle(id string, appID string, archivePath str UpdatedAt: &now, } - if util.IsHelmManaged() { - addSupportBundleToCache(&supportBundle) - return &supportBundle, nil - } - bundleMarshaled, err := json.Marshal(supportBundle) if err != nil { return nil, errors.Wrap(err, "failed to marshal support bundle") @@ -489,11 +405,6 @@ func (s *KOTSStore) UpdateSupportBundle(bundle *types.SupportBundle) error { now := time.Now() bundle.UpdatedAt = &now - if util.IsHelmManaged() { - addSupportBundleToCache(bundle) - return nil - } - supportBundleSecretMtx.Lock() defer supportBundleSecretMtx.Unlock() @@ -561,14 +472,6 @@ func (s *KOTSStore) GetSupportBundleArchive(bundleID string) (string, error) { } func (s *KOTSStore) GetSupportBundleAnalysis(id string) (*types.SupportBundleAnalysis, error) { - if util.IsHelmManaged() { - bundle := getSupportBundleFromCache(id) - if bundle == nil { - return nil, nil - } - return bundle.Analysis, nil - } - clientset, err := k8sutil.GetClientset() if err != nil { return nil, errors.Wrap(err, "failed to get clientset") @@ -606,15 +509,6 @@ func (s *KOTSStore) SetSupportBundleAnalysis(id string, results []byte) error { Insights: insights, } - if util.IsHelmManaged() { - bundle := getSupportBundleFromCache(id) - if bundle == nil { - return ErrNotFound - } - bundle.Analysis = &a - return nil - } - supportBundleSecretMtx.Lock() defer supportBundleSecretMtx.Unlock() diff --git a/pkg/supportbundle/helm.go b/pkg/supportbundle/helm.go deleted file mode 100644 index ca3e293963..0000000000 --- a/pkg/supportbundle/helm.go +++ /dev/null @@ -1,69 +0,0 @@ -package supportbundle - -import ( - "github.com/pkg/errors" - "github.com/replicatedhq/kots/pkg/helm" - "github.com/replicatedhq/kots/pkg/logger" - "github.com/replicatedhq/kots/pkg/util" - "github.com/replicatedhq/kotskinds/client/kotsclientset/scheme" - troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" - troubleshootclientsetscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme" - "github.com/replicatedhq/troubleshoot/pkg/docrewrite" - troubleshootsb "github.com/replicatedhq/troubleshoot/pkg/supportbundle" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func getSupportBundleSpecFromOCI(licenseID string, url string) (*troubleshootv1beta2.SupportBundle, *troubleshootv1beta2.Redactor, error) { - err := helm.CreateHelmRegistryCreds(licenseID, licenseID, url) - if err != nil { - return nil, nil, errors.Wrap(err, "failed to load collector spec") - } - - collectorContent, err := troubleshootsb.LoadSupportBundleSpec(url) - if err != nil { - return nil, nil, errors.Wrap(err, "failed to load collector spec") - } - - multidocs := util.ConvertToSingleDocs(collectorContent) - - // we support both raw collector kinds and supportbundle kinds here - supportBundle, err := troubleshootsb.ParseSupportBundleFromDoc(multidocs[0]) - if err != nil { - return nil, nil, errors.Wrap(err, "failed to parse collector") - } - - troubleshootclientsetscheme.AddToScheme(scheme.Scheme) - decode := scheme.Codecs.UniversalDeserializer().Decode - - redactors := &troubleshootv1beta2.Redactor{ - TypeMeta: metav1.TypeMeta{ - Kind: "Redactor", - APIVersion: "troubleshoot.sh/v1beta2", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "default-redactor", - }, - } - for i, additionalDoc := range multidocs { - if i == 0 { - continue - } - additionalDoc, err := docrewrite.ConvertToV1Beta2([]byte(additionalDoc)) - if err != nil { - logger.Infof("failed to convert doc %d to v1beta2: %v", i, err) - continue - } - obj, _, err := decode(additionalDoc, nil, nil) - if err != nil { - logger.Infof("failed to parse additional doc %d: %v", i, err) - continue - } - multidocRedactors, ok := obj.(*troubleshootv1beta2.Redactor) - if !ok { - continue - } - redactors.Spec.Redactors = append(redactors.Spec.Redactors, multidocRedactors.Spec.Redactors...) - } - - return supportBundle, redactors, nil -} diff --git a/pkg/supportbundle/spec.go b/pkg/supportbundle/spec.go index 181cff1d82..826651552a 100644 --- a/pkg/supportbundle/spec.go +++ b/pkg/supportbundle/spec.go @@ -17,14 +17,12 @@ import ( "github.com/pkg/errors" apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/k8sutil" kotstypes "github.com/replicatedhq/kots/pkg/kotsadm/types" "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/kurl" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/registry" - registrytypes "github.com/replicatedhq/kots/pkg/registry/types" "github.com/replicatedhq/kots/pkg/render/helper" "github.com/replicatedhq/kots/pkg/reporting" "github.com/replicatedhq/kots/pkg/snapshot" @@ -34,7 +32,6 @@ import ( "github.com/replicatedhq/kots/pkg/supportbundle/staticspecs" "github.com/replicatedhq/kots/pkg/supportbundle/types" "github.com/replicatedhq/kots/pkg/util" - "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" "github.com/replicatedhq/kotskinds/client/kotsclientset/scheme" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" sb "github.com/replicatedhq/troubleshoot/pkg/supportbundle" @@ -56,7 +53,7 @@ func init() { } // CreateRenderedSpec creates the support bundle specification from defaults and the kots app -func CreateRenderedSpec(app apptypes.AppType, sequence int64, kotsKinds *kotsutil.KotsKinds, opts types.TroubleshootOptions) (*troubleshootv1beta2.SupportBundle, error) { +func CreateRenderedSpec(app *apptypes.App, sequence int64, kotsKinds *kotsutil.KotsKinds, opts types.TroubleshootOptions) (*troubleshootv1beta2.SupportBundle, error) { builtBundle := kotsKinds.SupportBundle.DeepCopy() if builtBundle == nil { builtBundle = &troubleshootv1beta2.SupportBundle{ @@ -176,7 +173,7 @@ func mergeSupportBundleSpecs(builtBundles map[string]*troubleshootv1beta2.Suppor } // createSupportBundleSpecConfigMap creates a configmap containing the support bundle spec -func createSupportBundleSpecConfigMap(app apptypes.AppType, sequence int64, kotsKinds *kotsutil.KotsKinds, configMapName string, builtBundle *troubleshootv1beta2.SupportBundle, opts types.TroubleshootOptions, clientset kubernetes.Interface) error { +func createSupportBundleSpecConfigMap(app *apptypes.App, sequence int64, kotsKinds *kotsutil.KotsKinds, configMapName string, builtBundle *troubleshootv1beta2.SupportBundle, opts types.TroubleshootOptions, clientset kubernetes.Interface) error { s := serializer.NewYAMLSerializer(serializer.DefaultMetaFactory, scheme.Scheme, scheme.Scheme) var b bytes.Buffer if err := s.Encode(builtBundle, &b); err != nil { @@ -197,13 +194,9 @@ func createSupportBundleSpecConfigMap(app apptypes.AppType, sequence int64, kots return errors.Wrap(err, "failed to unmarshal rendered support bundle spec") } - var registrySettings registrytypes.RegistrySettings - if !util.IsHelmManaged() { - s, err := store.GetStore().GetRegistryDetailsForApp(app.GetID()) - if err != nil { - return errors.Wrap(err, "failed to get registry settings for app") - } - registrySettings = s + registrySettings, err := store.GetStore().GetRegistryDetailsForApp(app.GetID()) + if err != nil { + return errors.Wrap(err, "failed to get registry settings for app") } collectors, err := registry.UpdateCollectorSpecsWithRegistryData(supportBundle.Spec.Collectors, registrySettings, kotsKinds.Installation, kotsKinds.License, &kotsKinds.KotsApplication) @@ -261,7 +254,7 @@ func createSupportBundleSpecConfigMap(app apptypes.AppType, sequence int64, kots } // createSupportBundleSpecSecret creates a secret containing the support bundle spec -func createSupportBundleSpecSecret(app apptypes.AppType, sequence int64, kotsKinds *kotsutil.KotsKinds, secretName string, builtBundle *troubleshootv1beta2.SupportBundle, opts types.TroubleshootOptions, clientset kubernetes.Interface) error { +func createSupportBundleSpecSecret(app *apptypes.App, sequence int64, kotsKinds *kotsutil.KotsKinds, secretName string, builtBundle *troubleshootv1beta2.SupportBundle, opts types.TroubleshootOptions, clientset kubernetes.Interface) error { s := serializer.NewYAMLSerializer(serializer.DefaultMetaFactory, scheme.Scheme, scheme.Scheme) var b bytes.Buffer if err := s.Encode(builtBundle, &b); err != nil { @@ -282,13 +275,9 @@ func createSupportBundleSpecSecret(app apptypes.AppType, sequence int64, kotsKin return errors.Wrap(err, "failed to unmarshal rendered support bundle spec") } - var registrySettings registrytypes.RegistrySettings - if !util.IsHelmManaged() { - s, err := store.GetStore().GetRegistryDetailsForApp(app.GetID()) - if err != nil { - return errors.Wrap(err, "failed to get registry settings for app") - } - registrySettings = s + registrySettings, err := store.GetStore().GetRegistryDetailsForApp(app.GetID()) + if err != nil { + return errors.Wrap(err, "failed to get registry settings for app") } collectors, err := registry.UpdateCollectorSpecsWithRegistryData(supportBundle.Spec.Collectors, registrySettings, kotsKinds.Installation, kotsKinds.License, &kotsKinds.KotsApplication) @@ -346,7 +335,7 @@ func createSupportBundleSpecSecret(app apptypes.AppType, sequence int64, kotsKin } // addAfterCollectionSpec adds cluster specific and upload results URI to the support bundle -func addAfterCollectionSpec(app apptypes.AppType, b *troubleshootv1beta2.SupportBundle, opts types.TroubleshootOptions) *troubleshootv1beta2.SupportBundle { +func addAfterCollectionSpec(app *apptypes.App, b *troubleshootv1beta2.SupportBundle, opts types.TroubleshootOptions) *troubleshootv1beta2.SupportBundle { supportBundle := b.DeepCopy() // determine an upload URL @@ -398,7 +387,7 @@ func createVendorSpec(b *troubleshootv1beta2.SupportBundle) (*troubleshootv1beta } // createClusterSpecificSupportBundle creates a support bundle spec with only cluster specific collectors, analyzers and upload result URI. -func createClusterSpecificSpec(app apptypes.AppType, b *troubleshootv1beta2.SupportBundle, clientset kubernetes.Interface) (*troubleshootv1beta2.SupportBundle, error) { +func createClusterSpecificSpec(app *apptypes.App, b *troubleshootv1beta2.SupportBundle, clientset kubernetes.Interface) (*troubleshootv1beta2.SupportBundle, error) { supportBundle, err := staticspecs.GetClusterSpecificSpec() if err != nil { logger.Errorf("Failed to load cluster specific support bundle spec: %v", err) @@ -410,7 +399,7 @@ func createClusterSpecificSpec(app apptypes.AppType, b *troubleshootv1beta2.Supp } // createDefaultSpec creates a default support bundle spec that includes the default collectors and analyzers and add kurl specific collectors and analyzers if the cluster is a kurl cluster -func createDefaultSpec(app apptypes.AppType, b *troubleshootv1beta2.SupportBundle, opts types.TroubleshootOptions, namespacesToCollect []string, namespacesToAnalyze []string, clientset *kubernetes.Clientset) (*troubleshootv1beta2.SupportBundle, error) { +func createDefaultSpec(app *apptypes.App, b *troubleshootv1beta2.SupportBundle, opts types.TroubleshootOptions, namespacesToCollect []string, namespacesToAnalyze []string, clientset *kubernetes.Clientset) (*troubleshootv1beta2.SupportBundle, error) { supportBundle, err := staticspecs.GetDefaultSpec() if err != nil { logger.Errorf("Failed to load default support bundle spec: %v", err) @@ -456,7 +445,7 @@ func createDefaultSpec(app apptypes.AppType, b *troubleshootv1beta2.SupportBundl } func addDiscoveredSpecs( - supportBundle *troubleshootv1beta2.SupportBundle, app apptypes.AppType, clientset kubernetes.Interface, + supportBundle *troubleshootv1beta2.SupportBundle, app *apptypes.App, clientset kubernetes.Interface, ) *troubleshootv1beta2.SupportBundle { specs, err := findSupportBundleSpecs(clientset) if err != nil { @@ -725,29 +714,19 @@ func getDefaultAnalyzers(isKurl bool) []*troubleshootv1beta2.Analyze { // addDefaultDynamicTroubleshoot adds dynamic spec to the support bundle. // prefer addDefaultTroubleshoot unless absolutely necessary to encourage consistency across built-in and kots.io specs. -func addDefaultDynamicTroubleshoot(supportBundle *troubleshootv1beta2.SupportBundle, app apptypes.AppType, imageName string, pullSecret *troubleshootv1beta2.ImagePullSecrets) *troubleshootv1beta2.SupportBundle { +func addDefaultDynamicTroubleshoot(supportBundle *troubleshootv1beta2.SupportBundle, app *apptypes.App, imageName string, pullSecret *troubleshootv1beta2.ImagePullSecrets) *troubleshootv1beta2.SupportBundle { next := supportBundle.DeepCopy() next.Spec.Collectors = append(next.Spec.Collectors, getDefaultDynamicCollectors(app, imageName, pullSecret)...) next.Spec.Analyzers = append(next.Spec.Analyzers, getDefaultDynamicAnalyzers(app)...) return next } -func getDefaultDynamicCollectors(app apptypes.AppType, imageName string, pullSecret *troubleshootv1beta2.ImagePullSecrets) []*troubleshootv1beta2.Collect { +func getDefaultDynamicCollectors(app *apptypes.App, imageName string, pullSecret *troubleshootv1beta2.ImagePullSecrets) []*troubleshootv1beta2.Collect { collectors := make([]*troubleshootv1beta2.Collect, 0) - var err error - var license *v1beta1.License - switch a := app.(type) { - case *apptypes.App: - license, err = store.GetStore().GetLatestLicenseForApp(app.GetID()) - if err != nil { - logger.Errorf("Failed to load license data from store: %v", err) - } - case *apptypes.HelmApp: - license, err = helm.GetChartLicenseFromSecretOrDownload(a) - if err != nil { - logger.Errorf("Failed to load license data from helm: %v", err) - } + license, err := store.GetStore().GetLatestLicenseForApp(app.GetID()) + if err != nil { + logger.Errorf("Failed to load license data from store: %v", err) } if license != nil { @@ -829,25 +808,23 @@ func getDefaultDynamicCollectors(app apptypes.AppType, imageName string, pullSec collectors = append(collectors, makeVeleroCollectors()...) - if app, ok := app.(*apptypes.App); ok { - apps := []*apptypes.App{} - if app != nil { - apps = append(apps, app) - } else { - var err error - apps, err = store.GetStore().ListInstalledApps() - if err != nil { - logger.Errorf("Failed to list installed apps: %v", err) - } + apps := []*apptypes.App{} + if app != nil { + apps = append(apps, app) + } else { + var err error + apps, err = store.GetStore().ListInstalledApps() + if err != nil { + logger.Errorf("Failed to list installed apps: %v", err) } + } - if len(apps) > 0 { - appVersionArchiveCollectors, err := makeAppVersionArchiveCollectors(apps) - if err != nil { - logger.Errorf("Failed to make app version archive collectors: %v", err) - } - collectors = append(collectors, appVersionArchiveCollectors...) + if len(apps) > 0 { + appVersionArchiveCollectors, err := makeAppVersionArchiveCollectors(apps) + if err != nil { + logger.Errorf("Failed to make app version archive collectors: %v", err) } + collectors = append(collectors, appVersionArchiveCollectors...) } clientset, err := k8sutil.GetClientset() @@ -890,7 +867,7 @@ func getDefaultDynamicCollectors(app apptypes.AppType, imageName string, pullSec return collectors } -func getDefaultDynamicAnalyzers(app apptypes.AppType) []*troubleshootv1beta2.Analyze { +func getDefaultDynamicAnalyzers(app *apptypes.App) []*troubleshootv1beta2.Analyze { analyzers := make([]*troubleshootv1beta2.Analyze, 0) analyzers = append(analyzers, makeAPIReplicaAnalyzer()) diff --git a/pkg/supportbundle/supportbundle.go b/pkg/supportbundle/supportbundle.go index e6bf5410fa..188545eb1d 100644 --- a/pkg/supportbundle/supportbundle.go +++ b/pkg/supportbundle/supportbundle.go @@ -12,7 +12,6 @@ import ( "github.com/mholt/archiver/v3" "github.com/pkg/errors" apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/helm" "github.com/replicatedhq/kots/pkg/k8sutil" "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/kurl" @@ -71,29 +70,6 @@ func Collect(app *apptypes.App, clusterID string) (string, error) { return supportBundle.ID, nil } -func CollectHelm(app *apptypes.HelmApp) (string, error) { - opts := types.TroubleshootOptions{ - DisableUpload: true, - } - supportBundle, err := CreateSupportBundleDependencies(app, app.Version, opts) - if err != nil { - return "", errors.Wrap(err, "could not generate support bundle dependencies") - } - - supportBundle.ID = strings.ToLower(ksuid.New().String()) - supportBundle.Slug = supportBundle.ID - - err = store.GetStore().CreateInProgressSupportBundle(supportBundle) - if err != nil { - return "", errors.Wrap(err, "could not generate support bundle in progress") - } - - progressChan := executeUpdateRoutine(supportBundle) - executeSupportBundleCollectRoutine(supportBundle, progressChan) - - return supportBundle.ID, nil -} - // CreateBundle will create a support bundle in the store, attempting to use the // requestedID. This function uploads the archive and creates the record. func CreateBundle(requestedID string, appID string, archivePath string) (*types.SupportBundle, error) { @@ -137,23 +113,10 @@ func GetBundleCommand(appSlug string) []string { // CreateSupportBundleDependencies generates k8s secrets and configmaps for the support bundle spec and redactors. // These resources will be used when executing a support bundle collection -func CreateSupportBundleDependencies(app apptypes.AppType, sequence int64, opts types.TroubleshootOptions) (*types.SupportBundle, error) { - var kotsKinds *kotsutil.KotsKinds - switch a := app.(type) { - case *apptypes.App: - k, err := getKotsKindsForApp(a, sequence) - if err != nil { - return nil, errors.Wrap(err, "failed to get kots kinds for app") - } - kotsKinds = k - case *apptypes.HelmApp: - k, err := getKotsKindsForHelmApp(a) - if err != nil { - return nil, errors.Wrap(err, "failed to get kots kinds for helm") - } - kotsKinds = k - default: - return nil, errors.Errorf("cannot get kotskinds for app type %T", app) +func CreateSupportBundleDependencies(app *apptypes.App, sequence int64, opts types.TroubleshootOptions) (*types.SupportBundle, error) { + kotsKinds, err := getKotsKindsForApp(app, sequence) + if err != nil { + return nil, errors.Wrap(err, "failed to get kots kinds for app") } supportBundle, err := CreateRenderedSpec(app, sequence, kotsKinds, opts) @@ -219,32 +182,6 @@ func getKotsKindsForApp(app *apptypes.App, sequence int64) (*kotsutil.KotsKinds, return kotsKinds, nil } -func getKotsKindsForHelmApp(app *apptypes.HelmApp) (*kotsutil.KotsKinds, error) { - license, err := helm.GetChartLicenseFromSecretOrDownload(app) - if err != nil { - return nil, errors.Wrap(err, "failed to get license from secret") - } - - specURL := strings.TrimSuffix(app.ChartPath, fmt.Sprintf("/%s", app.Release.Chart.Name())) - upstreamSupportBundle, upstreamRedactors, err := getSupportBundleSpecFromOCI(license.Spec.LicenseID, specURL) - if err != nil { - // don't return; use default collectors/analyzers when spec cannot be downloaded - logger.Infof("failed to download support bundle spec from %s: %v", specURL, err) - } - - kotsKinds := kotsutil.EmptyKotsKinds() - kotsKinds.License = license - - if upstreamSupportBundle != nil { - kotsKinds.SupportBundle = upstreamSupportBundle - } - if upstreamRedactors != nil { - kotsKinds.Redactor = upstreamRedactors - } - - return &kotsKinds, nil -} - func getAnalysisFromBundle(archivePath string) ([]byte, error) { bundleDir, err := ioutil.TempDir("", "kots") if err != nil { diff --git a/pkg/updatechecker/updatechecker.go b/pkg/updatechecker/updatechecker.go index cb0508ac57..287eca9ebb 100644 --- a/pkg/updatechecker/updatechecker.go +++ b/pkg/updatechecker/updatechecker.go @@ -11,16 +11,12 @@ import ( downstreamtypes "github.com/replicatedhq/kots/pkg/api/downstream/types" "github.com/replicatedhq/kots/pkg/app" apptypes "github.com/replicatedhq/kots/pkg/app/types" - "github.com/replicatedhq/kots/pkg/helm" - "github.com/replicatedhq/kots/pkg/kotsadmconfig" license "github.com/replicatedhq/kots/pkg/kotsadmlicense" upstream "github.com/replicatedhq/kots/pkg/kotsadmupstream" - kotslicense "github.com/replicatedhq/kots/pkg/license" "github.com/replicatedhq/kots/pkg/logger" "github.com/replicatedhq/kots/pkg/preflight" "github.com/replicatedhq/kots/pkg/preflight/types" kotspull "github.com/replicatedhq/kots/pkg/pull" - registrytypes "github.com/replicatedhq/kots/pkg/registry/types" "github.com/replicatedhq/kots/pkg/reporting" kotssemver "github.com/replicatedhq/kots/pkg/semver" storepkg "github.com/replicatedhq/kots/pkg/store" @@ -66,7 +62,7 @@ func Start() error { // if enabled, and a cron job was found, update the existing cron job with the latest cron spec // if disabled: stop the current running cron job (if exists) // no-op for airgap applications -func Configure(a apptypes.AppType, updateCheckerSpec string) error { +func Configure(a *apptypes.App, updateCheckerSpec string) error { appId := a.GetID() appSlug := a.GetSlug() isAirgap := a.GetIsAirgap() @@ -211,98 +207,15 @@ func CheckForUpdates(opts CheckForUpdatesOpts) (ucr *UpdateCheckResponse, finalE tasks.StartUpdateTaskMonitor("update-download", finishedChan) - if util.IsHelmManaged() { - ucr, finalError = checkForHelmAppUpdates(opts, finishedChan) - if finalError != nil { - finalError = errors.Wrap(finalError, "failed to get helm app updates") - return - } - } else { - ucr, finalError = checkForKotsAppUpdates(opts, finishedChan) - if finalError != nil { - finalError = errors.Wrap(finalError, "failed to get kots app updates") - return - } + ucr, finalError = checkForKotsAppUpdates(opts, finishedChan) + if finalError != nil { + finalError = errors.Wrap(finalError, "failed to get kots app updates") + return } return } -func checkForHelmAppUpdates(opts CheckForUpdatesOpts, finishedChan chan<- error) (*UpdateCheckResponse, error) { - helmApp := helm.GetHelmApp(opts.AppID) - - license, err := helm.GetChartLicenseFromSecretOrDownload(helmApp) - if err != nil { - return nil, errors.Wrap(err, "failed to get license for helm app") - } - - if license == nil { - return nil, errors.Wrap(err, "license not found for helm app") - } - - currentVersion, err := semver.ParseTolerant(helmApp.Release.Chart.Metadata.Version) - if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("failed to get parse current version %s", helmApp.Release.Chart.Metadata.Version)) - } - - availableUpdateTags, err := helm.CheckForUpdates(helmApp, license, ¤tVersion) - if err != nil { - expiredErr := util.ActionableError{ - NoRetry: true, - Message: "License is expired", - } - isExpired := false - - newLicense, _ := helm.GetChartLicenseFromSecretOrDownload(helmApp) - if newLicense != nil { - isExpired, _ = kotslicense.LicenseIsExpired(newLicense) - } else { - isExpired, _ = kotslicense.LicenseIsExpired(license) - } - - if isExpired { - return nil, expiredErr - } - - return nil, errors.Wrap(err, "failed to get available updates") - } - - var updates []UpdateCheckRelease - for _, update := range availableUpdateTags { - updates = append(updates, UpdateCheckRelease{ - Sequence: 0, // TODO - Version: update.Tag, - }) - } - - ucr := UpdateCheckResponse{ - AvailableUpdates: int64(len(updates)), - AvailableReleases: updates, - DeployingRelease: nil, - } - - status := fmt.Sprintf("%d Updates available...", ucr.AvailableUpdates) - if err := store.SetTaskStatus("update-download", status, "running"); err != nil { - return nil, errors.Wrap(err, "failed to set task status") - } - - if opts.Wait { - if err := downloadHelmAppUpdates(opts, helmApp, license.Spec.LicenseID, updates); err != nil { - return nil, errors.Wrap(err, "failed to process updates") - } - } else if ucr.AvailableUpdates > 0 { - go func() { - defer close(finishedChan) - if err := downloadHelmAppUpdates(opts, helmApp, license.Spec.LicenseID, updates); err != nil { - logger.Error(errors.Wrap(err, "failed to process updates")) - } - finishedChan <- err - }() - } - - return &ucr, nil -} - func checkForKotsAppUpdates(opts CheckForUpdatesOpts, finishedChan chan<- error) (*UpdateCheckResponse, error) { a, err := store.GetApp(opts.AppID) if err != nil { @@ -403,13 +316,13 @@ func checkForKotsAppUpdates(opts CheckForUpdatesOpts, finishedChan chan<- error) } if opts.Wait { - if err := downloadKotsAppUpdates(opts, a.ID, d.ClusterID, filteredUpdates, updates.UpdateCheckTime); err != nil { + if err := downloadAppUpdates(opts, a.ID, d.ClusterID, filteredUpdates, updates.UpdateCheckTime); err != nil { return nil, errors.Wrap(err, "failed to download updates synchronously") } } else if ucr.AvailableUpdates > 0 { go func() { defer close(finishedChan) - err := downloadKotsAppUpdates(opts, a.ID, d.ClusterID, filteredUpdates, updates.UpdateCheckTime) + err := downloadAppUpdates(opts, a.ID, d.ClusterID, filteredUpdates, updates.UpdateCheckTime) if err != nil { logger.Error(errors.Wrap(err, "failed to download updates asynchronously")) } @@ -420,55 +333,7 @@ func checkForKotsAppUpdates(opts CheckForUpdatesOpts, finishedChan chan<- error) return &ucr, nil } -func downloadHelmAppUpdates(opts CheckForUpdatesOpts, helmApp *apptypes.HelmApp, licenseID string, updates []UpdateCheckRelease) error { - currentKotsKinds, err := helm.GetKotsKindsFromHelmApp(helmApp) - if err != nil { - return errors.Wrapf(err, "failed to get current config values") - } - - // Download in reverse order, from oldest to newest - for i := len(updates) - 1; i >= 0; i-- { - update := updates[i] - status := fmt.Sprintf("Downloading release %s...", update.Version) - if err := store.SetTaskStatus("update-download", status, "running"); err != nil { - logger.Info("failed to set task status", zap.String("error", err.Error())) - } - - kotsKinds, err := helm.GetKotsKindsFromUpstreamChartVersion(helmApp, licenseID, update.Version) - if err != nil { - return errors.Wrapf(err, "failed to pull update %s for chart", update.Version) - } - kotsKinds.ConfigValues = currentKotsKinds.ConfigValues.DeepCopy() - - downstreamStatus := storetypes.VersionPending - // TODO: preflight handling - // if kotsKinds.HasPreflights() { - // downstreamStatus = types.VersionPendingPreflight - // } - - sequence := int64(-1) // TODO: do something sensible, this value isn't used - registrySettings := registrytypes.RegistrySettings{} // TODO: private registries aren't supported yet - t, err := kotsadmconfig.NeedsConfiguration(helmApp.GetSlug(), sequence, helmApp.GetIsAirgap(), &kotsKinds, registrySettings) - if err != nil { - return errors.Wrap(err, "failed to check if version needs configuration") - } - if t { - downstreamStatus = storetypes.VersionPendingConfig - } - - replicatedMetadata, err := helm.GetReplicatedMetadataFromUpstreamChartVersion(helmApp, licenseID, update.Version) - if err != nil { - return errors.Wrap(err, "failed to replicated metadata") - } - - helm.SetCachedUpdateStatus(helmApp.ChartPath, update.Version, downstreamStatus) - helm.SetCachedUpdateMetadata(helmApp.ChartPath, update.Version, replicatedMetadata) - } - - return nil -} - -func downloadKotsAppUpdates(opts CheckForUpdatesOpts, appID string, clusterID string, updates []upstreamtypes.Update, updateCheckTime time.Time) error { +func downloadAppUpdates(opts CheckForUpdatesOpts, appID string, clusterID string, updates []upstreamtypes.Update, updateCheckTime time.Time) error { for index, update := range updates { appSequence, err := upstream.DownloadUpdate(appID, update, opts.SkipPreflights, opts.SkipCompatibilityCheck) if appSequence != nil { @@ -511,7 +376,7 @@ func ensureDesiredVersionIsDeployed(opts CheckForUpdatesOpts, clusterID string) return nil } - if !opts.IsAutomatic || util.IsHelmManaged() { + if !opts.IsAutomatic { return nil } diff --git a/pkg/util/util.go b/pkg/util/util.go index 0f2cb3ab79..2948ddce09 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -165,10 +165,6 @@ func HomeDir() string { return os.Getenv("USERPROFILE") } -func IsHelmManaged() bool { - return os.Getenv("IS_HELM_MANAGED") == "true" -} - func IsEmbeddedCluster() bool { return os.Getenv("EMBEDDED_CLUSTER_ID") != "" } diff --git a/web/src/Root.tsx b/web/src/Root.tsx index 2aa10be0e6..a6771247d3 100644 --- a/web/src/Root.tsx +++ b/web/src/Root.tsx @@ -99,7 +99,6 @@ type State = { featureFlags: object; fetchingMetadata: boolean; initSessionId: string | null; - isHelmManaged: boolean; selectedAppName: string | null; snapshotInProgressApps: string[]; themeState: ThemeState; @@ -124,7 +123,6 @@ const Root = () => { shouldShowClusterUpgradeModal: false, errLoggingOut: "", featureFlags: {}, - isHelmManaged: false, fetchingMetadata: false, initSessionId: Utilities.localStorageEnabled() ? localStorage.getItem(INIT_SESSION_ID_STORAGE_KEY) @@ -182,38 +180,6 @@ const Root = () => { setState({ initSessionId }); }; - // TODO: delete if not used - // const handleActiveInitSessionCompleted = () => { - // if (Utilities.localStorageEnabled()) { - // localStorage.removeItem(INIT_SESSION_ID_STORAGE_KEY); - // } - // setState({ initSessionId: "" }); - // }; - - const checkIsHelmManaged = async () => { - try { - const res = await fetch(`${process.env.API_ENDPOINT}/is-helm-managed`, { - headers: { - "Content-Type": "application/json", - }, - method: "GET", - credentials: "include", - }); - if (res.ok && res.status === 200) { - const response = await res.json(); - setState({ isHelmManaged: response.isHelmManaged }); - return response.isHelmManaged; - } else { - setState({ isHelmManaged: false }); - } - return false; - } catch (err) { - console.log(err); - setState({ isHelmManaged: false }); - return false; - } - }; - const getPendingApp = async () => { try { const res = await fetch(`${process.env.API_ENDPOINT}/pendingapp`, { @@ -360,7 +326,6 @@ const Root = () => { fetchKotsAppMetadata(); if (Utilities.isLoggedIn()) { ping(); - checkIsHelmManaged(); getAppsList().then((appsList) => { if (appsList?.length > 0 && window.location.pathname === "/apps") { const { slug } = appsList[0]; @@ -479,7 +444,6 @@ const Root = () => { onLogoutError={onLogoutError} isSnapshotsSupported={isSnapshotsSupported()} errLoggingOut={state.errLoggingOut} - isHelmManaged={state.isHelmManaged} />
@@ -502,7 +466,6 @@ const Root = () => { pendingApp={getPendingApp} onLoginSuccess={getAppsList} fetchingMetadata={state.fetchingMetadata} - checkIsHelmManaged={checkIsHelmManaged} navigate={navigate} /> } @@ -523,7 +486,6 @@ const Root = () => { } /> @@ -715,7 +677,6 @@ const Root = () => { refetchAppsList={getAppsList} snapshotInProgressApps={state.snapshotInProgressApps} ping={ping} - isHelmManaged={state.isHelmManaged} isEmbeddedCluster={Boolean( state.adminConsoleMetadata?.isEmbeddedCluster )} @@ -741,7 +702,6 @@ const Root = () => { refetchAppsList={getAppsList} snapshotInProgressApps={state.snapshotInProgressApps} ping={ping} - isHelmManaged={state.isHelmManaged} isEmbeddedCluster={Boolean( state.adminConsoleMetadata?.isEmbeddedCluster )} @@ -791,7 +751,6 @@ const Root = () => { } /> diff --git a/web/src/components/apps/AppDetailPage.tsx b/web/src/components/apps/AppDetailPage.tsx index 9b3656ecfd..1fe2c41284 100644 --- a/web/src/components/apps/AppDetailPage.tsx +++ b/web/src/components/apps/AppDetailPage.tsx @@ -24,7 +24,6 @@ type Props = { adminConsoleMetadata?: Metadata; appNameSpace: string | null; appName: string | null; - isHelmManaged: boolean; onActiveInitSession: (session: string) => void; ping: () => void; // TODO: remove this after adding app hook to Root- @@ -101,8 +100,6 @@ function AppDetailPage(props: Props) { // navigate to first app if available if (appsList && appsList?.length > 0) { navigate(`/app/${appsList[0].slug}`, { replace: true }); - } else if (props.isHelmManaged) { - navigate("/install-with-helm", { replace: true }); } else if (Utilities.isLoggedIn()) { navigate("/upload-license", { replace: true }); } else { @@ -411,7 +408,6 @@ function AppDetailPage(props: Props) { cluster: selectedApp?.downstream?.cluster, displayErrorModal: state.displayErrorModal, isBundleUploading: isBundleUploading, - isHelmManaged: props.isHelmManaged, isEmbeddedCluster: props.isEmbeddedCluster, isVeleroInstalled: isVeleroInstalled, logo: selectedApp?.iconUri, @@ -487,7 +483,6 @@ function AppDetailPage(props: Props) { activeTab={lastItem === params.slug ? "app" : lastItem} app={selectedApp} isVeleroInstalled={isVeleroInstalled} - isHelmManaged={props.isHelmManaged} isEmbeddedCluster={props.isEmbeddedCluster} /> diff --git a/web/src/components/apps/AppLicense.tsx b/web/src/components/apps/AppLicense.tsx index cd83b7aee7..b2b10e1be7 100644 --- a/web/src/components/apps/AppLicense.tsx +++ b/web/src/components/apps/AppLicense.tsx @@ -22,14 +22,11 @@ import "@src/scss/components/apps/AppLicense.scss"; import { LicenseFields } from "@features/Dashboard"; import { useLicenseWithIntercept } from "@features/App"; import Icon from "../Icon"; -import { UseDownloadValues } from "../hooks"; -import { HelmDeployModal } from "../shared/modals/HelmDeployModal"; type Props = { app: App; changeCallback: () => void; syncCallback: () => void; - isHelmManaged: boolean; }; type State = { @@ -318,65 +315,13 @@ const AppLicenseComponent = () => { ); } - const { app, isHelmManaged } = outletContext; + const { app } = outletContext; const expiresAt = getLicenseExpiryDate(appLicense); const gitops = app.downstream?.gitops; const appName = app?.name || "Your application"; let nextModalBody: ReactNode; - if (isHelmManaged) { - const sequence = app?.downstream?.currentVersion?.sequence; - const versionLabel = app?.downstream?.currentVersion?.versionLabel; - - nextModalBody = ( - - {({ - download, - downloadError: downloadError, - name, - ref, - url, - }: { - download: () => void; - clearError: () => void; - downloadError: boolean; - name: string; - ref: string; - url: string; - }) => { - return ( - <> - - - - ); - }} - - ); - } else if (gitops?.isConnected) { + if (gitops?.isConnected) { nextModalBody = (

diff --git a/web/src/components/apps/AppVersionHistory.tsx b/web/src/components/apps/AppVersionHistory.tsx index 69fff01985..a7418bff5f 100644 --- a/web/src/components/apps/AppVersionHistory.tsx +++ b/web/src/components/apps/AppVersionHistory.tsx @@ -30,8 +30,6 @@ import { Repeater } from "../../utilities/repeater"; import { AirgapUploader } from "../../utilities/airgapUploader"; import ReactTooltip from "react-tooltip"; import Pager from "../shared/Pager"; -import { HelmDeployModal } from "../shared/modals/HelmDeployModal"; -import { UseDownloadValues } from "../hooks"; import { KotsPageTitle } from "@components/Head"; import "@src/scss/components/apps/AppVersionHistory.scss"; @@ -64,7 +62,6 @@ type Props = { app: App; displayErrorModal: boolean; isBundleUploading: boolean; - isHelmManaged: boolean; makeCurrentVersion: ( slug: string, version: Version | null, @@ -132,8 +129,6 @@ type State = { showDeployWarningModal: boolean; showDiffErrModal: boolean; showDiffOverlay: boolean; - showHelmDeployModalForSequence: number | null; - showHelmDeployModalForVersionLabel: string; showLogsModal: boolean; showNoChangesModal: boolean; showSkipModal: boolean; @@ -153,13 +148,6 @@ type State = { yamlErrorDetails: string[]; }; -const filterNonHelmTabs = (tab: string, isHelmManaged: boolean) => { - if (isHelmManaged) { - return tab.startsWith("helm"); - } - return true; -}; - class AppVersionHistory extends Component { constructor(props: Props) { super(props); @@ -208,8 +196,6 @@ class AppVersionHistory extends Component { showDeployWarningModal: false, showDiffErrModal: false, showDiffOverlay: false, - showHelmDeployModalForSequence: null, - showHelmDeployModalForVersionLabel: "", showLogsModal: false, showNoChangesModal: false, showSkipModal: false, @@ -506,9 +492,6 @@ class AppVersionHistory extends Component {

{tabs .filter((tab) => tab !== "renderError") - .filter((tab) => - filterNonHelmTabs(tab, this.props.outletContext.isHelmManaged) - ) .map((tab) => (
{ try { const { app } = this.props.outletContext; let clusterId = app.downstream.cluster?.id; - if (this.props.outletContext.isHelmManaged) { - clusterId = 0; - } this.setState({ logsLoading: true, showLogsModal: true, @@ -1024,9 +1004,7 @@ class AppVersionHistory extends Component { if (isFailing) { selectedTab = Utilities.getDeployErrorTab(response.logs); } else { - selectedTab = Object.keys(response.logs).filter((tab) => - filterNonHelmTabs(tab, this.props.outletContext.isHelmManaged) - )[0]; + selectedTab = Object.keys(response.logs)[0]; } this.setState({ logs: response.logs, @@ -1292,17 +1270,8 @@ class AppVersionHistory extends Component { let allVersions = this.state.versionHistory; // exclude pinned version - if (this.props.outletContext.isHelmManaged) { - // Only show pending versions in the "New version available" card. Helm, unlike kots, always adds a new version, even when we rollback. - if (this.state.updatesAvailable && allVersions?.length > 0) { - if (allVersions[0].status.startsWith("pending")) { - allVersions = allVersions?.slice(1); - } - } - } else { - if (this.state.updatesAvailable) { - allVersions = this.state.versionHistory?.slice(1); - } + if (this.state.updatesAvailable) { + allVersions = this.state.versionHistory?.slice(1); } if (!allVersions?.length) { @@ -1353,34 +1322,7 @@ class AppVersionHistory extends Component { }); }; - handleActionButtonClicked = ( - versionLabel: string | null | undefined, - sequence: number - ) => { - if (this.props.outletContext.isHelmManaged && versionLabel) { - this.setState({ - showHelmDeployModalForVersionLabel: versionLabel, - showHelmDeployModalForSequence: sequence, - }); - } - }; - deployButtonStatus = (version: Version) => { - if (this.props.outletContext.isHelmManaged) { - const deployedSequence = - this.props.outletContext.app?.downstream?.currentVersion?.sequence; - - if (version.sequence > deployedSequence) { - return "Deploy"; - } - - if (version.sequence < deployedSequence) { - return "Rollback"; - } - - return "Redeploy"; - } - const app = this.props.outletContext.app; const downstream = app?.downstream; @@ -1447,13 +1389,6 @@ class AppVersionHistory extends Component { if (version.preflightResultCreatedAt) { newPreflightResults = secondsAgo(version.preflightResultCreatedAt) < 12; } - let isPending = false; - if ( - this.props.outletContext.isHelmManaged && - version.status.startsWith("pending") - ) { - isPending = true; - } return ( @@ -1463,12 +1398,6 @@ class AppVersionHistory extends Component { deployVersion={this.deployVersion} downloadVersion={this.downloadVersion} gitopsEnabled={gitopsIsConnected} - handleActionButtonClicked={() => - this.handleActionButtonClicked( - version.versionLabel, - version.sequence - ) - } handleSelectReleasesToDiff={this.handleSelectReleasesToDiff} handleViewLogs={this.handleViewLogs} isChecked={isChecked} @@ -1526,97 +1455,6 @@ class AppVersionHistory extends Component { } versionHistory={this.state.versionHistory} /> - {this.state.showHelmDeployModalForVersionLabel === - version.versionLabel && - this.state.showHelmDeployModalForSequence === version.sequence && ( - - {({ - download, - clearError: clearDownloadError, - downloadError: downloadError, - // isDownloading, - name, - ref, - url, - }: { - download: () => void; - clearError: () => void; - downloadError: boolean; - // isDownloading: boolean; - name: string; - ref: string; - url: string; - }) => { - return ( - <> - { - this.setState({ - showHelmDeployModalForVersionLabel: "", - }); - clearDownloadError(); - }} - registryUsername={ - this.props?.outletContext.app?.credentials?.username - } - registryPassword={ - this.props?.outletContext.app?.credentials?.password - } - revision={ - this.deployButtonStatus(version) === "Rollback" - ? version.sequence - : null - } - showHelmDeployModal={true} - showDownloadValues={ - this.deployButtonStatus(version) === "Deploy" - } - subtitle={ - this.deployButtonStatus(version) === "Rollback" - ? `Follow the steps below to rollback to revision ${version.sequence}.` - : this.deployButtonStatus(version) === "Redeploy" - ? "Follow the steps below to redeploy the release using the currently deployed chart version and values." - : "Follow the steps below to upgrade the release." - } - title={` ${this.deployButtonStatus(version)} ${ - this.props?.outletContext.app.slug - } ${ - this.deployButtonStatus(version) === "Deploy" - ? version.versionLabel - : "" - }`} - upgradeTitle={ - this.deployButtonStatus(version) === "Rollback" - ? "Rollback release" - : this.deployButtonStatus(version) === "Redeploy" - ? "Redeploy release" - : "Upgrade release" - } - version={version.versionLabel} - namespace={this.props?.outletContext.app?.namespace} - /> - - - ); - }} - - )} ); }; @@ -1703,23 +1541,10 @@ class AppVersionHistory extends Component { } let sequenceLabel = "Sequence"; - if (this.props.outletContext.isHelmManaged) { - sequenceLabel = "Revision"; - } - // In Helm, only pending versions are updates. In kots native, a deployed version can be an update after a rollback. let pendingVersion; - if (this.props.outletContext.isHelmManaged) { - if ( - this.state.updatesAvailable && - versionHistory[0].status.startsWith("pending") - ) { - pendingVersion = versionHistory[0]; - } - } else { - if (this.state.updatesAvailable) { - pendingVersion = versionHistory[0]; - } + if (this.state.updatesAvailable) { + pendingVersion = versionHistory[0]; } const renderVersionLabel = () => { @@ -2027,9 +1852,7 @@ class AppVersionHistory extends Component {
)}
- {versionHistory.length > 1 && - !gitopsIsConnected && - !this.props.outletContext.isHelmManaged + {versionHistory.length > 1 && !gitopsIsConnected ? this.renderDiffBtn() : null}
@@ -2353,7 +2176,6 @@ class AppVersionHistory extends Component { appSlug={app?.slug} autoDeploy={app?.autoDeploy} gitopsIsConnected={downstream?.gitops?.isConnected} - isHelmManaged={this.props.outletContext.isHelmManaged} isOpen={this.state.showAutomaticUpdatesModal} isSemverRequired={app?.isSemverRequired} onAutomaticUpdatesConfigured={() => { diff --git a/web/src/components/hooks/index.ts b/web/src/components/hooks/index.ts deleted file mode 100644 index ca372c08b6..0000000000 --- a/web/src/components/hooks/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { UseIsHelmManaged, useIsHelmManaged } from "./useIsHelmManaged"; -import { useSaveConfig } from "./useSaveConfig"; -import { useDownloadValues, UseDownloadValues } from "./useDownloadValues"; - -export { - UseIsHelmManaged, - useDownloadValues, - UseDownloadValues, - useIsHelmManaged, - useSaveConfig, -}; diff --git a/web/src/components/hooks/useDownloadValues.js b/web/src/components/hooks/useDownloadValues.js deleted file mode 100644 index 8e4c4021c9..0000000000 --- a/web/src/components/hooks/useDownloadValues.js +++ /dev/null @@ -1,122 +0,0 @@ -import { useState, useEffect } from "react"; - -const getValues = async ({ - _fetch = fetch, - apiEndpoint = process.env.API_ENDPOINT, - appSlug, - sequence, - versionLabel, - isPending, -}) => { - try { - const response = await _fetch( - `${apiEndpoint}/app/${appSlug}/values/${sequence}?isPending=${isPending}&semver=${versionLabel}`, - { - method: "GET", - headers: { - "Content-Type": "application/blob", - }, - credentials: "include", - } - ); - if (!response.ok) { - throw new Error("Error fetching values"); - } - - const data = await response.blob(); - return { data }; - } catch (error) { - throw Error(error); - } -}; - -const useDownloadValues = ({ - _createObjectURL = URL.createObjectURL, - _getValues = getValues, - _revokeObjectURL = URL.revokeObjectURL, - appSlug, - fileName, - sequence, - versionLabel, - isPending, -} = {}) => { - const [isDownloading, setIsDownloading] = useState(false); - const [error, setError] = useState(null); - const [url, setUrl] = useState(null); - const [name, setName] = useState(null); - - // creates a download url and adds it to the dom triggering download of file defined in url - useEffect(() => { - if (url) { - const link = document.createElement("a"); - link.href = url; - link.setAttribute("download", name); - - document.body.appendChild(link); - - link.click(); - link.parentNode.removeChild(link); - _revokeObjectURL(url); - setUrl(null); - } - }, [url]); - - const download = async () => { - try { - setIsDownloading(true); - setError(null); - // TODO: error will never be returned. probably refactor to return error or use react-query - const { data, error: _error } = await _getValues({ - appSlug, - sequence, - versionLabel, - isPending, - }); - if (_error) { - setError(_error); - setIsDownloading(false); - return; - } - - const newUrl = _createObjectURL(new Blob([data])); - setUrl(newUrl); - setName(fileName); - setIsDownloading(false); - } catch (downloadError) { - setIsDownloading(false); - setError(downloadError); - } - }; - - const clearError = () => { - setError(null); - }; - - return { - clearError, - download, - error, - isDownloading, - }; -}; - -function UseDownloadValues({ - appSlug, - fileName, - sequence, - versionLabel, - isPending, - children, -}) { - const query = useDownloadValues({ - appSlug, - fileName, - sequence, - versionLabel, - isPending, - }); - - return children(query); -} - -export { useDownloadValues, UseDownloadValues, getValues }; diff --git a/web/src/components/hooks/useDownloadValues.test.jsx b/web/src/components/hooks/useDownloadValues.test.jsx deleted file mode 100644 index d528d0da68..0000000000 --- a/web/src/components/hooks/useDownloadValues.test.jsx +++ /dev/null @@ -1,174 +0,0 @@ -/** - * @jest-environment jsdom - */ -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { act, renderHook } from "@testing-library/react-hooks"; -import { getValues, useDownloadValues } from "./useDownloadValues"; - -describe("useDownloadValues", () => { - describe("GET", () => { - let queryClient; - let wrapper; - beforeEach(() => { - queryClient = new QueryClient(); - wrapper = function wrapperFunc({ children }) { - return ( - - {children} - - ); - }; - }); - it("calls _getValues", async () => { - const fetchValuesSpy = jest.fn(() => Promise.resolve()); - const testConfig = { - _getValues: fetchValuesSpy, - _createObjectURL: jest.fn(() => "test"), - _revokeObjectURL: jest.fn(() => "test"), - appSlug: "test", - fileName: "test", - sequence: 1, - vaersionLabel: "1.2.3", - isPending: false, - }; - - const { result, waitFor } = renderHook( - () => useDownloadValues(testConfig), - { - wrapper, - } - ); - - await act(async () => { - await result.current.download(); - }); - await waitFor(() => result.current.isSuccess); - - const expectedFetchConfig = { - appSlug: testConfig.appSlug, - sequence: testConfig.sequence, - versionLabel: testConfig.versionLabel, - isPending: testConfig.isPending, - }; - - expect(result.current.variables).toEqual(undefined); - expect(fetchValuesSpy).toHaveBeenCalledTimes(1); - expect(fetchValuesSpy).toHaveBeenCalledWith(expectedFetchConfig); - }); - it.todo( - "test the rest of this or figure out how to use react-query with it" - ); - }); - describe("getValues", () => { - it("calls getValues with the correct url and configuration", async () => { - const expectedBody = { - test: "test", - }; - const blobSpy = jest.fn(() => Promise.resolve(expectedBody)); - const _fetchValuesSpy = jest.fn(() => - Promise.resolve({ - ok: true, - blob: blobSpy, - }) - ); - const testAppSlug = "testAppSlug"; - const testSequence = 1; - const versionLabel = "1.2.3"; - const isPending = false; - const testAPIEndpoint = "testAPIEndpoint"; - const testGetValuesConfig = { - _fetch: _fetchValuesSpy, - apiEndpoint: testAPIEndpoint, - appSlug: testAppSlug, - sequence: testSequence, - versionLabel: versionLabel, - isPending: isPending, - }; - - const expectedAPIEndpoint = `${testAPIEndpoint}/app/${testAppSlug}/values/${testSequence}?isPending=${isPending}&semver=${versionLabel}`; - const expectedResponse = { - data: expectedBody, - }; - const expectedFetchConfig = { - method: "GET", - headers: { - "Content-Type": "application/blob", - }, - credentials: "include", - }; - await expect(getValues(testGetValuesConfig)).resolves.toEqual( - expectedResponse - ); - expect(_fetchValuesSpy).toHaveBeenCalledTimes(1); - expect(_fetchValuesSpy).toHaveBeenCalledWith( - expectedAPIEndpoint, - expectedFetchConfig - ); - expect(blobSpy).toHaveBeenCalledTimes(1); - }); - - it("throws error when response is not ok", async () => { - const _fetchValuesSpy = jest.fn(() => - Promise.resolve({ - ok: false, - }) - ); - const testAppSlug = "testAppSlug"; - const testSequence = 1; - const testVersionLabel = "1.2.3"; - const testIsPending = false; - - const testAPIEndpoint = "testAPIEndpoint"; - const testGetValuesConfig = { - _fetch: _fetchValuesSpy, - apiEndpoint: testAPIEndpoint, - appSlug: testAppSlug, - sequence: testSequence, - versionLabel: testVersionLabel, - isPending: testIsPending, - }; - - await expect(getValues(testGetValuesConfig)).rejects.toThrowError( - "Error fetching values" - ); - }); - - it("throws error when response is not blob", async () => { - const _fetchValuesSpy = jest.fn(() => - Promise.resolve({ - ok: true, - blob: () => Promise.reject(new Error("Error parsing blob")), - }) - ); - - const testAppSlug = "testAppSlug"; - const testSequence = 1; - const testAPIEndpoint = "testAPIEndpoint"; - const testGetValuesConfig = { - _fetch: _fetchValuesSpy, - apiEndpoint: testAPIEndpoint, - appSlug: testAppSlug, - sequence: testSequence, - }; - - await expect(getValues(testGetValuesConfig)).rejects.toThrowError( - "Error parsing blob" - ); - }); - it("throws error when network error", async () => { - const _fetchValuesSpy = jest.fn(() => - Promise.reject(new Error("Error fetching")) - ); - - const testAPIEndpoint = "testAPIEndpoint"; - const testGetValuesConfig = { - _fetch: _fetchValuesSpy, - apiEndpoint: testAPIEndpoint, - }; - - await expect(getValues(testGetValuesConfig)).rejects.toThrowError( - "Error fetching" - ); - }); - }); -}); diff --git a/web/src/components/hooks/useIsHelmManaged.tsx b/web/src/components/hooks/useIsHelmManaged.tsx deleted file mode 100644 index 1b5011f6b6..0000000000 --- a/web/src/components/hooks/useIsHelmManaged.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useQuery, UseQueryResult } from "@tanstack/react-query"; - -interface IsHelmManagedResponse { - isHelmManaged: boolean; -} - -type IsHelmManaged = boolean; - -async function fetchIsHelmManaged({ - apiEndpoint = process.env.API_ENDPOINT, -} = {}): Promise { - try { - const res = await fetch(`${apiEndpoint}/is-helm-managed`, { - headers: { - "Content-Type": "application/json", - }, - method: "GET", - credentials: "include", - }); - if (res.ok) { - return await res.json(); - } - throw new Error("Error fetching isHelmManaged"); - } catch (err) { - if (err instanceof Error) - throw Error(err?.message || "Error fetching isHelmManaged"); - else throw Error("Error fetching isHelmManaged"); - } -} - -function useIsHelmManaged() { - return useQuery({ - queryKey: ["isHelmManaged"], - queryFn: () => fetchIsHelmManaged(), - staleTime: Infinity, - select: (response): IsHelmManaged => response.isHelmManaged || false, - }); -} - -function UseIsHelmManaged({ - children, -}: { - children: (props: UseQueryResult) => JSX.Element; -}) { - const query = useIsHelmManaged(); - - return children(query); -} - -export { UseIsHelmManaged, fetchIsHelmManaged, useIsHelmManaged }; diff --git a/web/src/components/hooks/useSaveConfig.js b/web/src/components/hooks/useSaveConfig.js deleted file mode 100644 index 9439afff46..0000000000 --- a/web/src/components/hooks/useSaveConfig.js +++ /dev/null @@ -1,33 +0,0 @@ -import { useMutation } from "@tanstack/react-query"; - -const putConfig = async ({ - _fetch = fetch, - apiEndpoint = process.env.API_ENDPOINT, - appSlug, - body, -}) => { - try { - const response = await _fetch(`${apiEndpoint}/app/${appSlug}/config`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - credentials: "include", - body, - }); - - if (!response.ok) { - throw new Error("Error saving config"); - } - - const data = await response.json(); - return { data }; - } catch (error) { - throw Error(error); - } -}; - -const useSaveConfig = ({ _putConfig = putConfig, appSlug } = {}) => - useMutation(({ body }) => _putConfig({ appSlug, body })); - -export { useSaveConfig, putConfig }; diff --git a/web/src/components/hooks/useSaveConfig.test.jsx b/web/src/components/hooks/useSaveConfig.test.jsx deleted file mode 100644 index 02e7447bde..0000000000 --- a/web/src/components/hooks/useSaveConfig.test.jsx +++ /dev/null @@ -1,157 +0,0 @@ -/** - * @jest-environment jsdom - */ -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { renderHook } from "@testing-library/react-hooks"; -import { useSaveConfig, putConfig } from "./useSaveConfig"; - -describe("useSaveConfig", () => { - describe("useSaveConfig", () => { - let queryClient; - let wrapper; - - beforeEach(() => { - queryClient = new QueryClient(); - wrapper = function wrapperFunc({ children }) { - return ( - - {children} - - ); - }; - }); - it("calls _putConfig", async () => { - const putConfigSpy = jest.fn(() => Promise.resolve()); - - const testBody = { - test: "test", - }; - const testConfig = { - appSlug: "test", - _putConfig: putConfigSpy, - }; - const { result, waitFor } = renderHook(() => useSaveConfig(testConfig), { - wrapper, - }); - - result.current.mutate({ body: testBody }); - - await waitFor(() => result.current.isSuccess); - - expect(result.current.variables).toEqual({ body: testBody }); - expect(putConfigSpy).toHaveBeenCalledTimes(1); - expect(putConfigSpy).toHaveBeenCalledWith({ - appSlug: testConfig.appSlug, - body: testBody, - }); - }); - }); - describe("putConfig", () => { - it("calls putConfig with the correct url and configuration", async () => { - const testBody = JSON.stringify({ - test: "test", - }); - const jsonSpy = jest.fn(() => Promise.resolve(testBody)); - const testFetch = jest.fn(() => - Promise.resolve({ - ok: true, - json: jsonSpy, - }) - ); - const testAppSlug = "testAppSlug"; - const testAPIEndpoint = "testAPIEndpoint"; - const testPutConfig = { - _fetch: testFetch, - appSlug: testAppSlug, - apiEndpoint: testAPIEndpoint, - body: testBody, - }; - - const expectedAPIEndpoint = `${testAPIEndpoint}/app/${testAppSlug}/config`; - const expectedResponse = { - data: testBody, - }; - const expectedFetchConfig = { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - credentials: "include", - body: testBody, - }; - await expect(putConfig(testPutConfig)).resolves.toEqual(expectedResponse); - expect(testFetch).toHaveBeenCalledTimes(1); - expect(testFetch).toHaveBeenCalledWith( - expectedAPIEndpoint, - expectedFetchConfig - ); - expect(jsonSpy).toHaveBeenCalledTimes(1); - }); - - it("throws error when response is not ok", async () => { - const testBody = JSON.stringify({ - test: "test", - }); - const testFetch = jest.fn(() => - Promise.resolve({ - ok: false, - }) - ); - const testAppSlug = "testAppSlug"; - const testAPIEndpoint = "testAPIEndpoint"; - const testPutConfig = { - _fetch: testFetch, - appSlug: testAppSlug, - apiEndpoint: testAPIEndpoint, - body: testBody, - }; - - await expect(putConfig(testPutConfig)).rejects.toThrowError( - "Error saving config" - ); - }); - - it("throws error when response is not json", async () => { - const testBody = JSON.stringify({ - test: "test", - }); - - const testFetch = jest.fn(() => - Promise.resolve({ - ok: true, - json: () => Promise.reject(new Error("Error parsing json")), - }) - ); - - const testAppSlug = "testAppSlug"; - const testAPIEndpoint = "testAPIEndpoint"; - const testPutConfig = { - _fetch: testFetch, - appSlug: testAppSlug, - apiEndpoint: testAPIEndpoint, - body: testBody, - }; - - await expect(putConfig(testPutConfig)).rejects.toThrowError( - "Error parsing json" - ); - }); - it("throws error when network error", async () => { - const testFetch = jest.fn(() => - Promise.reject(new Error("Error fetching")) - ); - - const testAppSlug = "testAppSlug"; - const testAPIEndpoint = "testAPIEndpoint"; - const testPutConfig = { - _fetch: testFetch, - appSlug: testAppSlug, - apiEndpoint: testAPIEndpoint, - }; - - await expect(putConfig(testPutConfig)).rejects.toThrowError( - "Error fetching" - ); - }); - }); -}); diff --git a/web/src/components/modals/AutomaticUpdatesModal.tsx b/web/src/components/modals/AutomaticUpdatesModal.tsx index 5f2812b0e8..fb4126fb9c 100644 --- a/web/src/components/modals/AutomaticUpdatesModal.tsx +++ b/web/src/components/modals/AutomaticUpdatesModal.tsx @@ -81,7 +81,6 @@ type Props = { appSlug: string; autoDeploy: string; gitopsIsConnected: boolean | undefined; - isHelmManaged: boolean; isOpen: boolean; isSemverRequired: boolean; onAutomaticUpdatesConfigured: () => void; @@ -268,13 +267,8 @@ export default class AutomaticUpdatesModal extends Component { }; render() { - const { - isOpen, - onRequestClose, - isSemverRequired, - gitopsIsConnected, - isHelmManaged, - } = this.props; + const { isOpen, onRequestClose, isSemverRequired, gitopsIsConnected } = + this.props; const { updateCheckerSpec, selectedSchedule, @@ -288,13 +282,7 @@ export default class AutomaticUpdatesModal extends Component { and whether updates will be deployed automatically.

); - if (isHelmManaged) { - configureText = ( -

- Configure how often you would like to automatically check for updates. -

- ); - } else if (gitopsIsConnected) { + if (gitopsIsConnected) { configureText = (

Configure how often you would like to automatically check for updates. @@ -367,7 +355,7 @@ export default class AutomaticUpdatesModal extends Component {

- {!gitopsIsConnected && !isHelmManaged && ( + {!gitopsIsConnected && (

Automatically deploy new versions diff --git a/web/src/components/shared/EditConfigIcon.tsx b/web/src/components/shared/EditConfigIcon.tsx index 76e505edec..f96002965d 100644 --- a/web/src/components/shared/EditConfigIcon.tsx +++ b/web/src/components/shared/EditConfigIcon.tsx @@ -1,7 +1,6 @@ import { useSelectedApp } from "@features/App"; import { Version } from "@types"; import { Link } from "react-router-dom"; -import { useIsHelmManaged } from "@src/components//hooks"; import Icon from "@components/Icon"; import ReactTooltip from "react-tooltip"; @@ -12,8 +11,6 @@ const EditConfigIcon = ({ version: Version | null; isPending: boolean; }) => { - const { data: isHelmManagedResponse } = useIsHelmManaged(); - const isHelmManaged = isHelmManagedResponse || {}; const selectedApp = useSelectedApp(); if (!version) { @@ -31,7 +28,7 @@ const EditConfigIcon = ({ } let url = `/app/${selectedApp?.slug}/config/${version.sequence}`; - if (isHelmManaged && version.status.startsWith("pending")) { + if (version.status.startsWith("pending")) { url = `${url}?isPending=${isPending}&semver=${version.semver}`; } diff --git a/web/src/components/shared/NavBar.tsx b/web/src/components/shared/NavBar.tsx index 4495573810..3521d6eb3c 100644 --- a/web/src/components/shared/NavBar.tsx +++ b/web/src/components/shared/NavBar.tsx @@ -16,7 +16,6 @@ type Props = { errLoggingOut: string; fetchingMetadata: boolean; isGitOpsSupported: boolean; - isHelmManaged: boolean; isIdentityServiceSupported: boolean; isKurlEnabled: boolean; isEmbeddedClusterEnabled: boolean; @@ -291,7 +290,6 @@ export class NavBar extends PureComponent { <> diff --git a/web/src/components/shared/NavBarDropdown.jsx b/web/src/components/shared/NavBarDropdown.jsx index ee62dd1595..6e487d0958 100644 --- a/web/src/components/shared/NavBarDropdown.jsx +++ b/web/src/components/shared/NavBarDropdown.jsx @@ -3,7 +3,7 @@ import Icon from "../Icon"; import ChangePasswordModal from "../modals/ChangePasswordModal/ChangePasswordModal"; import { useEffect, useRef, useState } from "react"; -const NavBarDropdown = ({ handleLogOut, isHelmManaged, isEmbeddedCluster }) => { +const NavBarDropdown = ({ handleLogOut, isEmbeddedCluster }) => { const [showDropdown, setShowDropdown] = useState(false); const [showModal, setShowModal] = useState(false); const testRef = useRef(null); @@ -49,7 +49,7 @@ const NavBarDropdown = ({ handleLogOut, isHelmManaged, isEmbeddedCluster }) => {

  • setShowModal(true)}>Change password

  • - {!isHelmManaged && !isEmbeddedCluster && ( + {!isEmbeddedCluster && (
  • Add new application

  • diff --git a/web/src/components/shared/SubNavBar/SubNavBar.jsx b/web/src/components/shared/SubNavBar/SubNavBar.jsx index 63c7a341ff..c221aa24b1 100644 --- a/web/src/components/shared/SubNavBar/SubNavBar.jsx +++ b/web/src/components/shared/SubNavBar/SubNavBar.jsx @@ -12,7 +12,6 @@ export default function SubNavBar({ isVeleroInstalled, isAccess = false, isSnapshots = false, - isHelmManaged, isEmbeddedCluster, }) { let { slug } = app; @@ -126,7 +125,6 @@ export default function SubNavBar({ return ( link.displayRule({ app: app || {}, - isHelmManaged, isEmbeddedCluster, isIdentityServiceSupported: app.isAppIdentityServiceSupported, diff --git a/web/src/components/shared/modals/HelmDeployModal.test.js b/web/src/components/shared/modals/HelmDeployModal.test.js deleted file mode 100644 index eb811db1f3..0000000000 --- a/web/src/components/shared/modals/HelmDeployModal.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe("HelmDeployModal", () => { - it.todo("refactor component and add unit tests"); -}); diff --git a/web/src/components/shared/modals/HelmDeployModal.tsx b/web/src/components/shared/modals/HelmDeployModal.tsx deleted file mode 100644 index d35b87e78c..0000000000 --- a/web/src/components/shared/modals/HelmDeployModal.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import Modal from "react-modal"; -// TODO: add type to CodeSnippet -// @ts-ignore -import CodeSnippet from "@src/components/shared/CodeSnippet"; -import "./styles/HelmDeployModal.scss"; - -function makeDeployCommand({ - appSlug, - chartPath, - revision = null, - showDownloadValues, - version, - namespace, -}: { - appSlug: string; - chartPath: string; - revision: number | null; - showDownloadValues: boolean; - version?: string; - namespace: string; -}) { - if (revision) { - return `helm -n ${namespace} rollback ${appSlug} ${revision}`; - } - - if (showDownloadValues) { - return `helm -n ${namespace} upgrade ${appSlug} ${chartPath} --version ${version} -f `; - } - - return `helm -n ${namespace} upgrade ${appSlug} ${chartPath} --reuse-values --version ${version}`; -} - -function makeLoginCommand({ - registryHostname = "", - registryUsername, - registryPassword, -}: { - registryHostname?: string; - registryUsername?: string; - registryPassword?: string; -} = {}) { - return `helm registry login ${ - registryHostname.slice(6).split("/")[0] - } --username ${registryUsername} --password ${registryPassword}`; -} - -function HelmDeployModal({ - appSlug, - chartPath, - downloadClicked = () => {}, - downloadError = false, - hideHelmDeployModal = () => {}, - // TODO: add downloading state - // isDownloading = false, - saveError = false, - showHelmDeployModal, - subtitle, - registryUsername = "myUsername", - registryPassword = "myPassword", - revision = null, - title, - upgradeTitle, - showDownloadValues = false, - version, - namespace, -}: { - appSlug: string; - chartPath: string; - downloadClicked: () => void; - downloadError: boolean; - hideHelmDeployModal: () => void; - // isDownloading: boolean; - saveError?: boolean; - showHelmDeployModal?: boolean; - subtitle: string; - registryUsername: string; - registryPassword: string; - revision?: number | null; - title: string; - upgradeTitle: string; - showDownloadValues: boolean; - version?: string; - namespace: string; -}) { - return ( - -
    -

    {title}

    -

    {subtitle}

    -
    -
    -
    -
    - 1 -
    - - Log in to the registry - - - Command has been copied to your clipboard - - } - > - {makeLoginCommand({ - registryHostname: chartPath, - registryUsername, - registryPassword, - })} - -
    -
    - {showDownloadValues && ( -
    - 2 -
    - - Download your new values.yaml file - - - {downloadError && ( - - There was a problem downloading your values.yaml file. Try - again. - - )} -
    -
    - )} -
    - - {showDownloadValues ? "3" : "2"} - -
    - - {upgradeTitle} - - {showDownloadValues && ( -

    - Ensure you replace {""} with - the path to your saved file. -

    - )} - - Command has been copied to your clipboard - - } - > - {makeDeployCommand({ - appSlug, - chartPath, - revision, - showDownloadValues, - version, - namespace, - })} - -
    -
    -
    - - {saveError && ( - - There was a problem saving your configuration. Close this modal and - try again. - - )} -
    -
    - ); -} - -export { HelmDeployModal }; diff --git a/web/src/components/tree/KotsApplicationTree.tsx b/web/src/components/tree/KotsApplicationTree.tsx index f8f67b8029..7ed974b87a 100644 --- a/web/src/components/tree/KotsApplicationTree.tsx +++ b/web/src/components/tree/KotsApplicationTree.tsx @@ -24,7 +24,6 @@ type Props = { app: App; appName: string; appNameSpace: string; - isHelmManaged: boolean; }; }; @@ -115,18 +114,16 @@ class KotsApplicationTree extends Component { return (
    - {!this.props.outletContext.isHelmManaged && ( -
    - Need to edit these files?{" "} - - Click here - {" "} - to learn how -
    - )} +
    + Need to edit these files?{" "} + + Click here + {" "} + to learn how +
    diff --git a/web/src/components/troubleshoot/GenerateSupportBundle.jsx b/web/src/components/troubleshoot/GenerateSupportBundle.jsx index b2ea0664a6..170a4e56d0 100644 --- a/web/src/components/troubleshoot/GenerateSupportBundle.jsx +++ b/web/src/components/troubleshoot/GenerateSupportBundle.jsx @@ -256,11 +256,7 @@ class GenerateSupportBundle extends Component { collectBundle = (clusterId) => { const { navigate } = this.props; - let url = `${process.env.API_ENDPOINT}/troubleshoot/supportbundle/app/${this.props.outletContext?.watch?.id}/cluster/${clusterId}/collect`; - if (!this.props.outletContext?.watch.id) { - // TODO: check if helm managed, not if id is missing - url = `${process.env.API_ENDPOINT}/troubleshoot/supportbundle/app/${this.props.outletContext?.watch?.slug}/collect`; - } + const url = `${process.env.API_ENDPOINT}/troubleshoot/supportbundle/app/${this.props.outletContext?.watch?.id}/cluster/${clusterId}/collect`; fetch(url, { headers: { diff --git a/web/src/components/troubleshoot/GenerateSupportBundleModal.tsx b/web/src/components/troubleshoot/GenerateSupportBundleModal.tsx index 712e945dd0..b452dd3ba6 100644 --- a/web/src/components/troubleshoot/GenerateSupportBundleModal.tsx +++ b/web/src/components/troubleshoot/GenerateSupportBundleModal.tsx @@ -174,11 +174,7 @@ const GenerateSupportBundleModal = ({ }, [state.supportBundles]); const collectBundle = (clusterId: number | undefined) => { - let url = `${process.env.API_ENDPOINT}/troubleshoot/supportbundle/app/${selectedApp?.id}/cluster/${clusterId}/collect`; - if (!selectedApp?.id) { - // TODO: check if helm managed, not if id is missing - url = `${process.env.API_ENDPOINT}/troubleshoot/supportbundle/app/${selectedApp?.slug}/collect`; - } + const url = `${process.env.API_ENDPOINT}/troubleshoot/supportbundle/app/${selectedApp?.id}/cluster/${clusterId}/collect`; fetch(url, { headers: { diff --git a/web/src/config-ui/subNavConfig.js b/web/src/config-ui/subNavConfig.js index 8041c0c455..d7b6242304 100644 --- a/web/src/config-ui/subNavConfig.js +++ b/web/src/config-ui/subNavConfig.js @@ -43,10 +43,9 @@ export default [ tabName: "license", displayName: "License", to: (slug) => `/app/${slug}/license`, - displayRule: ({ app, isHelmManaged }) => + displayRule: ({ app }) => app?.upstreamUri?.startsWith("replicated://") || - getApplicationType(app) === "replicated.app" || - isHelmManaged, + getApplicationType(app) === "replicated.app", }, { tabName: "state", @@ -71,8 +70,7 @@ export default [ tabName: "registry-settings", displayName: "Registry settings", to: (slug) => `/app/${slug}/registry-settings`, - displayRule: ({ isHelmManaged, isEmbeddedCluster }) => - !isHelmManaged && !isEmbeddedCluster, + displayRule: ({ isEmbeddedCluster }) => !isEmbeddedCluster, }, { tabName: "access", diff --git a/web/src/features/AppConfig/components/AppConfig.tsx b/web/src/features/AppConfig/components/AppConfig.tsx index 53bb342a9c..3e21b0ef67 100644 --- a/web/src/features/AppConfig/components/AppConfig.tsx +++ b/web/src/features/AppConfig/components/AppConfig.tsx @@ -10,12 +10,6 @@ import map from "lodash/map"; import Modal from "react-modal"; import Loader from "../../../components/shared/Loader"; import ErrorModal from "../../../components/modals/ErrorModal"; -import { HelmDeployModal } from "../../../components/shared/modals/HelmDeployModal"; -import { - UseIsHelmManaged, - useDownloadValues, - useSaveConfig, -} from "../../../components/hooks"; import ConfigInfo from "./ConfigInfo"; import "../../../scss/components/watches/WatchConfig.scss"; @@ -31,7 +25,6 @@ type Props = { params: KotsParams; app: App; fromLicenseFlow: boolean; - isHelmManaged: boolean; refreshAppData: () => void; refetchApps: () => void; navigate: ReturnType; @@ -88,7 +81,6 @@ type State = { initialConfigGroups: ConfigGroup[]; savingConfig: boolean; showConfigError: boolean; - showHelmDeployModal: boolean; showNextStepModal: boolean; showValidationError: boolean; }; @@ -119,7 +111,6 @@ class AppConfig extends Component { showValidationError: false, initialConfigGroups: [], savingConfig: false, - showHelmDeployModal: false, showNextStepModal: false, }; @@ -349,7 +340,7 @@ class AppConfig extends Component { configErrorMessage: "", }); - const { fromLicenseFlow, navigate, params, isHelmManaged } = this.props; + const { fromLicenseFlow, navigate, params } = this.props; const sequence = this.getSequence(); const { slug } = this.props.params; const createNewVersion = !fromLicenseFlow && params.sequence == undefined; @@ -407,13 +398,6 @@ class AppConfig extends Component { await this.props.refreshAppData(); } - if (isHelmManaged) { - this.setState({ - showHelmDeployModal: true, - }); - return; - } - if (fromLicenseFlow) { const app = await this.fetchApp(); const hasPreflight = app?.hasPreflight; @@ -660,7 +644,7 @@ class AppConfig extends Component { showNextStepModal, showValidationError, } = this.state; - const { fromLicenseFlow, params, isHelmManaged } = this.props; + const { fromLicenseFlow, params } = this.props; if (configLoading || !app) { return ( @@ -681,12 +665,7 @@ class AppConfig extends Component { downstreamVersionLabel = urlParams.get("semver") || ""; } - const isPending = urlParams.get("isPending") === "true"; - let saveButtonText = fromLicenseFlow ? "Continue" : "Save config"; - if (isHelmManaged) { - saveButtonText = "Generate Upgrade Command"; - } const sections = document.querySelectorAll(".observe-elements"); @@ -819,104 +798,50 @@ class AppConfig extends Component { })}
    - - {({ data: isHelmManagedFromHook }) => { - const { isError: saveError } = useSaveConfig({ - appSlug: this.props.params.slug, - }); - - const { download, clearError: clearDownloadError } = - useDownloadValues({ - appSlug: this.props.params.slug, - fileName: "values.yaml", - sequence: params.sequence, - versionLabel: downstreamVersionLabel, - isPending: isPending, - }); - - return ( - <> - {!isHelmManagedFromHook && ( - + +
    +
    + +
    +
    + {savingConfig && ( +
    + +
    + )} + {!savingConfig && ( +
    + {(showConfigError || this.state.showValidationError) && ( + + {configErrorMessage || validationErrorMessage} + )} -
    -
    - -
    -
    - {savingConfig && ( -
    - -
    - )} - {!savingConfig && ( -
    - {(showConfigError || - this.state.showValidationError) && ( - - {configErrorMessage || validationErrorMessage} - - )} - -
    - )} -
    -
    - {this.state.showHelmDeployModal && ( - <> - { - this.setState({ showHelmDeployModal: false }); - clearDownloadError(); - }} - registryUsername={ - this.props?.app?.credentials?.username || "" - } - registryPassword={ - this.props?.app?.credentials?.password || "" - } - saveError={saveError} - showHelmDeployModal={true} - showDownloadValues={true} - subtitle="Follow the steps below to upgrade the release with your new values.yaml." - title={`Upgrade ${this.props?.app?.slug}`} - upgradeTitle="Upgrade release" - version={downstreamVersionLabel || ""} - namespace={this.props?.app?.namespace || ""} - downloadError={false} - revision={null} - /> - - )} - - ); - }} - + {saveButtonText} + +
    + )} +
    +
    {" "}
    diff --git a/web/src/features/AppVersionHistory/AppVersionHistoryRow.tsx b/web/src/features/AppVersionHistory/AppVersionHistoryRow.tsx index c1757a06c4..5676676545 100644 --- a/web/src/features/AppVersionHistory/AppVersionHistoryRow.tsx +++ b/web/src/features/AppVersionHistory/AppVersionHistoryRow.tsx @@ -13,7 +13,6 @@ import Icon from "@src/components/Icon"; import { ViewDiffButton } from "@features/VersionDiff/ViewDiffButton"; import { Metadata, Version, VersionDownloadStatus } from "@types"; -import { useIsHelmManaged } from "@components/hooks"; import { useSelectedApp } from "@features/App/hooks/useSelectedApp"; import PreflightIcon from "@features/App/PreflightIcon"; @@ -22,7 +21,6 @@ interface Props { deployVersion: (version: Version) => void; downloadVersion: (version: Version) => void; gitopsEnabled: boolean; - handleActionButtonClicked: () => void; handleSelectReleasesToDiff: (version: Version, isChecked: boolean) => void; handleViewLogs: (version: Version | null, isFailing: boolean) => void; isChecked: boolean; @@ -56,7 +54,6 @@ function AppVersionHistoryRow(props: Props) { !props.version.source?.includes("Online Install") ); - const { data: isHelmManaged } = useIsHelmManaged(); const selectedApp = useSelectedApp(); useEffect(() => { @@ -77,23 +74,6 @@ function AppVersionHistoryRow(props: Props) { }; const deployButtonStatus = (version: Version) => { - if (isHelmManaged) { - const deployedSequence = - selectedApp?.downstream?.currentVersion?.sequence; - - if (!deployedSequence) throw new Error("deployedSequence is undefined"); - - if (version.sequence > deployedSequence) { - return "Deploy"; - } - - if (version.sequence < deployedSequence) { - return "Rollback"; - } - - return "Redeploy"; - } - const downstream = selectedApp?.downstream; const isCurrentVersion = @@ -172,9 +152,6 @@ function AppVersionHistoryRow(props: Props) { }; const isActionButtonDisabled = (version: Version) => { - if (isHelmManaged) { - return false; - } if ( Utilities.isPendingClusterUpgrade(selectedApp) && version.status === "deployed" @@ -200,11 +177,7 @@ function AppVersionHistoryRow(props: Props) { // useDeployAppVersion let actionFn = props.deployVersion; - if (isHelmManaged) { - actionFn = () => {}; - // TODO: conditionally fetch the admin console update status when mounting the hook - // by using verision.needsKotsUpgrade - } else if (version.needsKotsUpgrade) { + if (version.needsKotsUpgrade) { // postUpdateAdminConsole actionFn = props.upgradeAdminConsole; } else if (version.status === "pending_download") { @@ -263,10 +236,7 @@ function AppVersionHistoryRow(props: Props) { const preflightState = getPreflightState(version); - let configScreenURL = `/app/${selectedApp?.slug}/config/${version.sequence}`; - if (isHelmManaged && version.status.startsWith("pending")) { - configScreenURL = `${configScreenURL}?isPending=true&semver=${version.semver}`; - } + const configScreenURL = `/app/${selectedApp?.slug}/config/${version.sequence}`; // CONNECTED TO GITOPS // if (downstream?.gitops?.isConnected) { @@ -409,7 +379,6 @@ function AppVersionHistoryRow(props: Props) { })} disabled={isActionButtonDisabled(version)} onClick={() => { - props.handleActionButtonClicked(); if (needsConfiguration) { props?.navigate(configScreenURL); return null; @@ -616,16 +585,6 @@ function AppVersionHistoryRow(props: Props) { newPreflightResults, } = props; - let showSequence = true; - if (isHelmManaged && version.status.startsWith("pending")) { - showSequence = false; - } - - let sequenceLabel = "Sequence"; - if (isHelmManaged) { - sequenceLabel = "Revision"; - } - // Old Helm charts will not have any timestamps, so don't show current time when they are missing because it's misleading. let releasedTs = ""; const tsFormat = "MM/DD/YY @ hh:mm a z"; @@ -677,14 +636,12 @@ function AppVersionHistoryRow(props: Props) { )}
    {" "} - {showSequence && ( -

    - {sequenceLabel} {version.sequence} -

    - )} +

    + Sequence {version.sequence} +

    {releasedTs && (

    {" "} diff --git a/web/src/features/AppVersionHistory/api/getVersions.tsx b/web/src/features/AppVersionHistory/api/getVersions.tsx index 22891372cb..16e084f8d2 100644 --- a/web/src/features/AppVersionHistory/api/getVersions.tsx +++ b/web/src/features/AppVersionHistory/api/getVersions.tsx @@ -4,7 +4,6 @@ import { Utilities } from "../../../utilities/utilities"; import { useParams } from "react-router-dom"; import { useSelectedApp } from "@features/App"; import { useMetadata } from "@src/stores"; -import { useIsHelmManaged } from "@src/components/hooks"; import { App, KotsParams, Metadata, Version } from "@types"; async function getVersions({ @@ -125,62 +124,18 @@ function getVersionsSelectorForAirgapped({ return getVersionsSelectorForKotsManaged({ versions, selectedApp, metadata }); } -function getVersionsSelectorForHelmManaged({ - versions, -}: { - versions: { versionHistory: Version[] }; -}) { - const deployedSequence = versions?.versionHistory?.find( - (v) => v.status === "deployed" - )?.sequence; - - const versionHistory = versions?.versionHistory.map((version) => { - let statusLabel = "Redeploy"; - - if (deployedSequence === undefined) - return { - ...version, - statusLabel, - }; - - if (version.sequence > deployedSequence) { - statusLabel = "Deploy"; - } - - if (version.sequence < deployedSequence) { - statusLabel = "Rollback"; - } - return { - ...version, - statusLabel, - }; - }); - - return { - ...versions, - versionHistory, - }; -} - function chooseVersionsSelector({ isAirgap, isKurl, - isHelmManaged, }: { isAirgap?: boolean; isKurl?: boolean; - isHelmManaged?: boolean; }) { // if airgapped if (isAirgap && isKurl) { return getVersionsSelectorForAirgapped; } - // if helm managed - if (isHelmManaged) { - return getVersionsSelectorForHelmManaged; - } - // if kots managed return getVersionsSelectorForKotsManaged; } @@ -195,12 +150,10 @@ function useVersions({ let { slug } = useParams(); let selectedApp = useSelectedApp(); let { data: metadata } = useMetadata(); - let { data: isHelmManaged } = useIsHelmManaged(); const versionSelector = chooseVersionsSelector({ // labels differ by installation manager and if airgapped isAirgap: metadata?.isAirgap, - isHelmManaged, isKurl: metadata?.isKurl, }); diff --git a/web/src/features/Auth/components/SecureAdminConsole.tsx b/web/src/features/Auth/components/SecureAdminConsole.tsx index 064bedcdf5..3c8fc3db98 100644 --- a/web/src/features/Auth/components/SecureAdminConsole.tsx +++ b/web/src/features/Auth/components/SecureAdminConsole.tsx @@ -12,7 +12,6 @@ type Props = { fetchingMetadata: boolean; onLoginSuccess: () => Promise; pendingApp: () => Promise; - checkIsHelmManaged: () => Promise; logo: string | null; navigate: ReturnType; }; @@ -53,7 +52,6 @@ class SecureAdminConsole extends Component { if (Utilities.localStorageEnabled()) { loggedIn = true; window.localStorage.setItem("isLoggedIn", "true"); - const isHelmManaged = await this.props.checkIsHelmManaged(); if (data.sessionRoles) { window.localStorage.setItem("session_roles", data.sessionRoles); @@ -70,8 +68,6 @@ class SecureAdminConsole extends Component { this.props.navigate(`/${pendingApp.slug}/airgap-bundle`, { replace: true, }); - } else if (isHelmManaged) { - this.props.navigate("install-with-helm", { replace: true }); } else { this.props.navigate("upload-license", { replace: true }); } diff --git a/web/src/features/Dashboard/api/getSelectedAppClusterDashboard.ts b/web/src/features/Dashboard/api/getSelectedAppClusterDashboard.ts index 038f19cb8e..fe7ebbab7e 100644 --- a/web/src/features/Dashboard/api/getSelectedAppClusterDashboard.ts +++ b/web/src/features/Dashboard/api/getSelectedAppClusterDashboard.ts @@ -1,5 +1,4 @@ import { useQuery } from "@tanstack/react-query"; -import { useIsHelmManaged } from "@components/hooks"; import { useSelectedApp } from "@features/App"; import { DashboardResponse } from "@types"; import axios from "axios"; @@ -9,11 +8,9 @@ axios.defaults.withCredentials = true; export const getSelectedAppClusterDashboard = async ({ appSlug, clusterId, - isHelmManaged, }: { appSlug: string; clusterId: string; - isHelmManaged: boolean; }): Promise => { const config = { headers: { @@ -21,10 +18,9 @@ export const getSelectedAppClusterDashboard = async ({ }, withCredentials: true, }; - const clusterIdToQuery = isHelmManaged && clusterId === "" ? 0 : clusterId; try { const res = await axios.get( - `${process.env.API_ENDPOINT}/app/${appSlug}/cluster/${clusterIdToQuery}/dashboard`, + `${process.env.API_ENDPOINT}/app/${appSlug}/cluster/${clusterId}/dashboard`, config ); @@ -48,8 +44,6 @@ export const useSelectedAppClusterDashboard = ({ }: { refetchInterval: number | false; }) => { - const { data: isHelmManaged = false } = useIsHelmManaged(); - const selectedApp = useSelectedApp(); const { slug } = selectedApp || { slug: "" }; const clusterId = selectedApp?.downstream?.cluster?.id.toString() || ""; @@ -59,7 +53,6 @@ export const useSelectedAppClusterDashboard = ({ getSelectedAppClusterDashboard({ appSlug: slug, clusterId, - isHelmManaged, }), { refetchInterval, diff --git a/web/src/features/Dashboard/components/Dashboard.tsx b/web/src/features/Dashboard/components/Dashboard.tsx index 42425a3d3c..bbdfde37d7 100644 --- a/web/src/features/Dashboard/components/Dashboard.tsx +++ b/web/src/features/Dashboard/components/Dashboard.tsx @@ -53,7 +53,6 @@ type OutletContext = { id: "" | number; }; isBundleUploading: boolean; - isHelmManaged: boolean; isEmbeddedCluster: boolean; isVeleroInstalled: boolean; makeCurrentVersion: ( @@ -157,7 +156,6 @@ const Dashboard = () => { const { app, isBundleUploading, - isHelmManaged, isEmbeddedCluster, isVeleroInstalled, makeCurrentVersion, @@ -730,7 +728,6 @@ const Dashboard = () => {

    )} @@ -865,7 +862,6 @@ const Dashboard = () => { hideAutomaticUpdatesModal(); refreshAppData(); }} - isHelmManaged={isHelmManaged} /> )} diff --git a/web/src/features/Dashboard/components/DashboardGraphsCard.tsx b/web/src/features/Dashboard/components/DashboardGraphsCard.tsx index e489218ad8..173173ed99 100644 --- a/web/src/features/Dashboard/components/DashboardGraphsCard.tsx +++ b/web/src/features/Dashboard/components/DashboardGraphsCard.tsx @@ -27,7 +27,6 @@ dayjs.extend(localizedFormat); type Props = { prometheusAddress: string; - isHelmManaged: boolean; metrics: Chart[]; }; @@ -279,9 +278,6 @@ export default class DashboardGraphsCard extends Component { }; render() { - if (this.props.isHelmManaged === true) { - return
    ; - } const { prometheusAddress, metrics } = this.props; const { promValue, showConfigureGraphs, savingPromError, savingPromValue } = this.state; diff --git a/web/src/features/Dashboard/components/DashboardVersionCard.tsx b/web/src/features/Dashboard/components/DashboardVersionCard.tsx index 33f031e543..928966214d 100644 --- a/web/src/features/Dashboard/components/DashboardVersionCard.tsx +++ b/web/src/features/Dashboard/components/DashboardVersionCard.tsx @@ -1,4 +1,4 @@ -import { RefObject, useEffect, useReducer } from "react"; +import { useEffect, useReducer } from "react"; import { Link, useNavigate, useParams } from "react-router-dom"; import ReactTooltip from "react-tooltip"; import DashboardGitOpsCard from "./DashboardGitOpsCard"; @@ -12,13 +12,10 @@ import ShowDetailsModal from "@src/components/modals/ShowDetailsModal"; import ShowLogsModal from "@src/components/modals/ShowLogsModal"; import DeployWarningModal from "@src/components/shared/modals/DeployWarningModal"; import SkipPreflightsModal from "@src/components/shared/modals/SkipPreflightsModal"; -import { HelmDeployModal } from "@src/components/shared/modals/HelmDeployModal"; import classNames from "classnames"; -import { UseDownloadValues } from "@src/components//hooks"; import { getReadableGitOpsProviderName } from "@src/utilities/utilities"; import { useNextAppVersionWithIntercept } from "../api/useNextAppVersion"; import { useSelectedApp } from "@features/App"; -import { useIsHelmManaged } from "@src/components//hooks"; import { Utilities, @@ -102,8 +99,6 @@ type State = { showDeployWarningModal: boolean; showDiffErrModal: boolean; showDiffModal: boolean; - showHelmDeployModal: boolean; - showHelmDeployModalWithVersionLabel?: string; showLogsModal: boolean; showNoChangesModal: boolean; showReleaseNotes: boolean; @@ -117,13 +112,6 @@ type State = { yamlErrorDetails: string[]; }; -const filterNonHelmTabs = (tab: string, isHelmManaged: boolean) => { - if (isHelmManaged) { - return tab.startsWith("helm"); - } - return true; -}; - const DashboardVersionCard = (props: Props) => { const [state, setState] = useReducer( (currentState: State, newState: Partial) => ({ @@ -159,8 +147,6 @@ const DashboardVersionCard = (props: Props) => { showDiffErrModal: false, showDiffModal: false, showDeployWarningModal: false, - showHelmDeployModal: false, - showHelmDeployModalWithVersionLabel: "", showLogsModal: false, showNoChangesModal: false, showReleaseNotes: false, @@ -182,8 +168,6 @@ const DashboardVersionCard = (props: Props) => { } = useNextAppVersionWithIntercept(); const { latestDeployableVersion } = newAppVersionWithInterceptData || {}; - const { data: isHelmManaged = false } = useIsHelmManaged(); - // moving this out of the state because new repeater instances were getting created // and it doesn't really affect the UI const versionDownloadStatusJobs: { @@ -270,7 +254,6 @@ const DashboardVersionCard = (props: Props) => {
    {tabs .filter((tab) => tab !== "renderError") - .filter((tab) => filterNonHelmTabs(tab, isHelmManaged)) .map((tab) => (
    { } try { let clusterId = selectedApp?.downstream?.cluster?.id; - if (isHelmManaged) { - clusterId = 0; - } + setState({ logsLoading: true, showLogsModal: true, @@ -319,9 +300,7 @@ const DashboardVersionCard = (props: Props) => { if (isFailing) { selectedTab = Utilities.getDeployErrorTab(response.logs); } else { - selectedTab = Object.keys(response.logs).filter((tab) => - filterNonHelmTabs(tab, isHelmManaged) - )[0]; + selectedTab = Object.keys(response.logs)[0]; } setState({ logs: response.logs, @@ -597,13 +576,6 @@ const DashboardVersionCard = (props: Props) => { continueWithFailedPreflights = false, redeploy = false ) => { - if (isHelmManaged) { - setState({ - showHelmDeployModal: true, - showHelmDeployModalWithVersionLabel: version?.versionLabel, - }); - return; - } const clusterSlug = selectedApp?.downstream?.cluster?.slug; if (!clusterSlug) { return; @@ -655,11 +627,6 @@ const DashboardVersionCard = (props: Props) => { const renderCurrentVersion = () => { const { currentVersion } = props; - let sequenceLabel = "Sequence"; - if (isHelmManaged) { - sequenceLabel = "Revision"; - } - return (
    @@ -669,7 +636,7 @@ const DashboardVersionCard = (props: Props) => { {currentVersion?.versionLabel || currentVersion?.appTitle}

    - {sequenceLabel} {currentVersion?.sequence} + Sequence {currentVersion?.sequence}

    {getCurrentVersionStatus(currentVersion)}
    @@ -763,12 +730,12 @@ const DashboardVersionCard = (props: Props) => { } else if (diffSummary) { return (
    - {!isHelmManaged && diffSummary.filesChanged > 0 ? ( + {diffSummary.filesChanged > 0 ? (
    {diffSummary.filesChanged} files changed{" "} - {!isHelmManaged && !downstream?.gitops?.isConnected && ( + {!downstream?.gitops?.isConnected && ( { }; const isActionButtonDisabled = (version: Version) => { - if (isHelmManaged) { - return false; - } if (state.versionDownloadStatuses?.[version.sequence]?.downloadingVersion) { return true; } @@ -1157,11 +1121,6 @@ const DashboardVersionCard = (props: Props) => { const isPendingDownload = version.status === "pending_download"; const isSecondaryActionBtn = needsConfiguration || isPendingDownload; - let url = `/app/${selectedApp?.slug}/config/${version.sequence}`; - if (isHelmManaged) { - url = `${url}?isPending=true&semver=${version.versionLabel}`; - } - return (
    {renderReleaseNotes(version)} @@ -1176,7 +1135,9 @@ const DashboardVersionCard = (props: Props) => { disabled={isActionButtonDisabled(version)} onClick={() => { if (needsConfiguration) { - navigate(url); + navigate( + `/app/${selectedApp?.slug}/config/${version.sequence}` + ); return; } if (version.needsKotsUpgrade) { @@ -1405,11 +1366,9 @@ const DashboardVersionCard = (props: Props) => { {latestDeployableVersion.versionLabel || latestDeployableVersion.title}

    - {isHelmManaged || ( -

    - Sequence {latestDeployableVersion.sequence} -

    - )} +

    + Sequence {latestDeployableVersion.sequence} +

    {latestDeployableVersion.isRequired && ( {" "} @@ -1498,11 +1457,6 @@ const DashboardVersionCard = (props: Props) => { ); } - let isPending = false; - if (isHelmManaged && latestDeployableVersion?.status?.startsWith("pending")) { - isPending = true; - } - return (
    @@ -1797,70 +1751,6 @@ const DashboardVersionCard = (props: Props) => { onForceDeployClick={onForceDeployClick} /> )} - {state.showHelmDeployModal && ( - - {({ - download, - error: downloadError, - name, - ref, - url, - }: { - download: () => void; - clearError: () => void; - error: string; - isDownloading: boolean; - name: string; - ref: RefObject; - url: string; - }) => { - const showDownloadValues = - state.showHelmDeployModalWithVersionLabel === - latestDeployableVersion?.versionLabel; - return ( - <> - { - setState({ - showHelmDeployModal: false, - }); - }} - registryUsername={selectedApp?.credentials?.username || ""} - registryPassword={selectedApp?.credentials?.password || ""} - showHelmDeployModal={true} - showDownloadValues={showDownloadValues} - subtitle={ - showDownloadValues - ? "Follow the steps below to upgrade the release." - : "Follow the steps below to redeploy the release using the currently deployed chart version and values." - } - title={ - showDownloadValues - ? `Deploy ${selectedApp?.slug} ${state.showHelmDeployModalWithVersionLabel}` - : `Redeploy ${selectedApp?.slug}` - } - upgradeTitle={ - showDownloadValues ? "Upgrade release" : "Redeploy release" - } - version={state.showHelmDeployModalWithVersionLabel || ""} - namespace={selectedApp?.namespace || ""} - /> - - - ); - }} - - )} {state.showDiffModal && ( void; @@ -39,11 +38,9 @@ function getPreviousSequence(versionHistory: Version[], version: Version) { function ViewDiffButton(props: Props) { const selectedApp = useSelectedApp(); - const { data: isHelmManaged = false } = useIsHelmManaged(); // TODO: flatten in selector - const showViewDiffButton = - !isHelmManaged && !selectedApp?.downstream.gitops?.isConnected; + const showViewDiffButton = !selectedApp?.downstream.gitops?.isConnected; const showDiffSummaryError = props.version?.diffSummaryError?.length > 0 ? true : false; const numberOfFilesChanged = props.version?.diffSummary diff --git a/web/src/stories/AutomaticUpdatesModal.stories.tsx b/web/src/stories/AutomaticUpdatesModal.stories.tsx index 7f82c5fd92..a8177547e4 100644 --- a/web/src/stories/AutomaticUpdatesModal.stories.tsx +++ b/web/src/stories/AutomaticUpdatesModal.stories.tsx @@ -27,5 +27,4 @@ AutomaticUpdatesModalExample.args = { isSemverRequired: false, gitopsIsConnected: false, onAutomaticUpdatesConfigured: () => {}, - isHelmManaged: false, }; diff --git a/web/src/stories/HelmDeployModal.stories.tsx b/web/src/stories/HelmDeployModal.stories.tsx deleted file mode 100644 index 7aa0956298..0000000000 --- a/web/src/stories/HelmDeployModal.stories.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { ComponentStory, ComponentMeta } from "@storybook/react"; -import { MemoryRouter } from "react-router-dom"; -import { HelmDeployModal } from "@src/components/shared/modals/HelmDeployModal"; - -export default { - title: "Example/HelmDeployModal", - component: HelmDeployModal, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ( - - - -); - -export const HelmDeployModalExample = Template.bind({}); - -HelmDeployModalExample.args = { - appSlug: "appslug", - chartPath: "chart.sentry.io/", - downloadClicked: () => alert("download clicked"), - downloadError: false, - hideHelmDeployModal: () => alert("hide helm deploy modal"), - saveError: false, - showHelmDeployModal: true, - subtitle: "Follow the steps below to upgrade the release.", - registryUsername: "myUsername", - registryPassword: "myPassword", - revision: null, - title: "Deploy Sentry", - upgradeTitle: "", - showDownloadValues: true, - version: "1.0", - namespace: "default", -}; diff --git a/web/src/stories/ShowLogsModal.stories.tsx b/web/src/stories/ShowLogsModal.stories.tsx index 815d7748dd..6a1344162a 100644 --- a/web/src/stories/ShowLogsModal.stories.tsx +++ b/web/src/stories/ShowLogsModal.stories.tsx @@ -11,21 +11,12 @@ export default { const Template: ComponentStory = (args) => { const [selectedTab, setSelectedTab] = useState("dryrunStdout"); const renderLogsTab = () => { - const isHelmManaged = false; - const filterNonHelmTabs = (tab: string) => { - if (isHelmManaged) { - return tab.startsWith("helm"); - } - return true; - }; - const tabs = Object.keys(args.logs); return (
    {tabs .filter((tab) => tab !== "renderError") - .filter((tab) => filterNonHelmTabs(tab)) .map((tab) => (