From c83a64ae57017289513dc726c2ef4fbdf5647124 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Mon, 28 Oct 2024 17:57:04 -0400 Subject: [PATCH 1/7] automatically deploy embedded cluster apps with provided config values --- pkg/airgap/airgap.go | 2 +- pkg/kotsadmconfig/config.go | 1 + pkg/online/online.go | 2 +- pkg/util/util.go | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/airgap/airgap.go b/pkg/airgap/airgap.go index 5600e0c9e5..5010c2ef82 100644 --- a/pkg/airgap/airgap.go +++ b/pkg/airgap/airgap.go @@ -280,7 +280,7 @@ func CreateAppFromAirgap(opts CreateAirgapAppOpts) (finalError error) { return errors.Wrap(err, "failed to get downstream version status") } - if status == storetypes.VersionPendingClusterManagement { + if status == storetypes.VersionPendingClusterManagement && configFile == "" { // if pending cluster management, we don't want to deploy the app return nil } diff --git a/pkg/kotsadmconfig/config.go b/pkg/kotsadmconfig/config.go index 3b335ffa38..cce6f780f8 100644 --- a/pkg/kotsadmconfig/config.go +++ b/pkg/kotsadmconfig/config.go @@ -166,6 +166,7 @@ func UpdateAppConfigValues(values map[string]kotsv1beta1.ConfigValue, configGrou return values } +// this is where config values that are passed to the install command are read from func ReadConfigValuesFromInClusterSecret() (string, error) { log := logger.NewCLILogger(os.Stdout) diff --git a/pkg/online/online.go b/pkg/online/online.go index 0b707bcb27..ccaae0268a 100644 --- a/pkg/online/online.go +++ b/pkg/online/online.go @@ -219,7 +219,7 @@ func CreateAppFromOnline(opts CreateOnlineAppOpts) (_ *kotsutil.KotsKinds, final return nil, errors.Wrap(err, "failed to get downstream version status") } - if status == storetypes.VersionPendingClusterManagement { + if status == storetypes.VersionPendingClusterManagement && configFile == "" { // if pending cluster management, we don't want to deploy the app return kotsKinds, nil } diff --git a/pkg/util/util.go b/pkg/util/util.go index 6fd7d111cb..3c0f572b7d 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -166,7 +166,7 @@ func HomeDir() string { } func IsEmbeddedCluster() bool { - return os.Getenv("EMBEDDED_CLUSTER_ID") != "" + return EmbeddedClusterID() != "" } func EmbeddedClusterID() string { From 2fd8310a4a02340c757c38f0901287434f51f008 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Mon, 28 Oct 2024 18:01:08 -0400 Subject: [PATCH 2/7] expand comment --- pkg/airgap/airgap.go | 3 ++- pkg/online/online.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/airgap/airgap.go b/pkg/airgap/airgap.go index 5010c2ef82..b309702e24 100644 --- a/pkg/airgap/airgap.go +++ b/pkg/airgap/airgap.go @@ -281,7 +281,8 @@ func CreateAppFromAirgap(opts CreateAirgapAppOpts) (finalError error) { } if status == storetypes.VersionPendingClusterManagement && configFile == "" { - // if pending cluster management, we don't want to deploy the app + // if pending cluster management, this is embedded cluster and we don't want to deploy the app before the user has added nodes + // if the config file is set, we assume that the user is OK with deploying the app on a single node return nil } diff --git a/pkg/online/online.go b/pkg/online/online.go index ccaae0268a..7cbe5ebf27 100644 --- a/pkg/online/online.go +++ b/pkg/online/online.go @@ -220,7 +220,8 @@ func CreateAppFromOnline(opts CreateOnlineAppOpts) (_ *kotsutil.KotsKinds, final } if status == storetypes.VersionPendingClusterManagement && configFile == "" { - // if pending cluster management, we don't want to deploy the app + // if pending cluster management, this is embedded cluster and we don't want to deploy the app before the user has added nodes + // if the config file is set, we assume that the user is OK with deploying the app on a single node return kotsKinds, nil } From 9c91e288be0d9e45e063fed8a3af8d8e616cfa77 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Tue, 29 Oct 2024 16:29:06 -0400 Subject: [PATCH 3/7] undo logic changes --- pkg/airgap/airgap.go | 5 ++--- pkg/online/online.go | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pkg/airgap/airgap.go b/pkg/airgap/airgap.go index b309702e24..5600e0c9e5 100644 --- a/pkg/airgap/airgap.go +++ b/pkg/airgap/airgap.go @@ -280,9 +280,8 @@ func CreateAppFromAirgap(opts CreateAirgapAppOpts) (finalError error) { return errors.Wrap(err, "failed to get downstream version status") } - if status == storetypes.VersionPendingClusterManagement && configFile == "" { - // if pending cluster management, this is embedded cluster and we don't want to deploy the app before the user has added nodes - // if the config file is set, we assume that the user is OK with deploying the app on a single node + if status == storetypes.VersionPendingClusterManagement { + // if pending cluster management, we don't want to deploy the app return nil } diff --git a/pkg/online/online.go b/pkg/online/online.go index 7cbe5ebf27..0b707bcb27 100644 --- a/pkg/online/online.go +++ b/pkg/online/online.go @@ -219,9 +219,8 @@ func CreateAppFromOnline(opts CreateOnlineAppOpts) (_ *kotsutil.KotsKinds, final return nil, errors.Wrap(err, "failed to get downstream version status") } - if status == storetypes.VersionPendingClusterManagement && configFile == "" { - // if pending cluster management, this is embedded cluster and we don't want to deploy the app before the user has added nodes - // if the config file is set, we assume that the user is OK with deploying the app on a single node + if status == storetypes.VersionPendingClusterManagement { + // if pending cluster management, we don't want to deploy the app return kotsKinds, nil } From e5406b4b64d653c96eb5b3ea3550f5ef00522321 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Tue, 29 Oct 2024 17:05:41 -0400 Subject: [PATCH 4/7] add admin-console confirm-management command --- cmd/kots/cli/embedded-cluster-management.go | 111 ++++++++++++++++++++ cmd/kots/cli/embedded-cluster.go | 36 +++++++ 2 files changed, 147 insertions(+) create mode 100644 cmd/kots/cli/embedded-cluster-management.go create mode 100644 cmd/kots/cli/embedded-cluster.go diff --git a/cmd/kots/cli/embedded-cluster-management.go b/cmd/kots/cli/embedded-cluster-management.go new file mode 100644 index 0000000000..9a50da9b98 --- /dev/null +++ b/cmd/kots/cli/embedded-cluster-management.go @@ -0,0 +1,111 @@ +package cli + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + + cursor "github.com/ahmetalpbalkan/go-cursor" + "github.com/pkg/errors" + "github.com/replicatedhq/kots/pkg/auth" + "github.com/replicatedhq/kots/pkg/k8sutil" + "github.com/replicatedhq/kots/pkg/logger" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func EmbeddedClusterConfirmManagementCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "confirm-management", + Short: "Confirm that the cluster is ready to deploy the application (that there are enough nodes, etc.)", + Long: "", + SilenceUsage: true, + SilenceErrors: false, + PreRun: func(cmd *cobra.Command, args []string) { + viper.BindPFlags(cmd.Flags()) + }, + RunE: func(cmd *cobra.Command, args []string) error { + v := viper.GetViper() + + if len(args) < 1 { + cmd.Help() + os.Exit(1) + } + + fmt.Print(cursor.Hide()) + defer fmt.Print(cursor.Show()) + + log := logger.NewCLILogger(cmd.OutOrStdout()) + namespace, err := getNamespaceOrDefault(v.GetString("namespace")) + if err != nil { + return errors.Wrap(err, "failed to get namespace") + } + + clientset, err := k8sutil.GetClientset() + if err != nil { + return errors.Wrap(err, "failed to get clientset") + } + + getPodName := func() (string, error) { + return k8sutil.WaitForKotsadm(clientset, namespace, time.Second*5) + } + + stopCh := make(chan struct{}) + defer close(stopCh) + + log.ActionWithoutSpinner("Confirming cluster management...") + + localPort, errChan, err := k8sutil.PortForward(0, 3000, namespace, getPodName, false, stopCh, log) + if err != nil { + return errors.Wrap(err, "failed to start port forwarding") + } + + go func() { + select { + case err := <-errChan: + if err != nil { + log.Error(err) + } + case <-stopCh: + } + }() + + authSlug, err := auth.GetOrCreateAuthSlug(clientset, namespace) + if err != nil { + return errors.Wrap(err, "failed to get kotsadm auth slug") + } + + requestPayload := map[string]interface{}{} // empty payload + requestBody, err := json.Marshal(requestPayload) + if err != nil { + return errors.Wrap(err, "failed to marshal request json") + } + + url := fmt.Sprintf("http://localhost:%d/api/v1/embedded-cluster/management", localPort) + newRequest, err := http.NewRequest("POST", url, bytes.NewBuffer(requestBody)) + if err != nil { + return errors.Wrap(err, "failed to create http request") + } + newRequest.Header.Add("Authorization", authSlug) + newRequest.Header.Add("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(newRequest) + if err != nil { + return errors.Wrap(err, "failed to execute http request") + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected status code %d from %s", resp.StatusCode, url) + } + + log.ActionWithoutSpinner("Done") + + return nil + }, + } + return cmd +} diff --git a/cmd/kots/cli/embedded-cluster.go b/cmd/kots/cli/embedded-cluster.go new file mode 100644 index 0000000000..5963233505 --- /dev/null +++ b/cmd/kots/cli/embedded-cluster.go @@ -0,0 +1,36 @@ +package cli + +import ( + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func EmbeddedClusterCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "embedded-cluster", + Short: "Configure embedded-cluster resources", + Long: ``, + Hidden: true, + DisableFlagsInUseLine: true, // removes "kots set [flags]" from usage in help output + + SilenceUsage: true, + SilenceErrors: false, + PreRun: func(cmd *cobra.Command, args []string) { + viper.BindPFlags(cmd.Flags()) + }, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + cmd.Help() + os.Exit(1) + } + + return nil + }, + } + + cmd.AddCommand(EmbeddedClusterConfirmManagementCmd()) + + return cmd +} From 11f81ff06ad695c468fcaee63eca9f281fbe9fd9 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Tue, 29 Oct 2024 17:49:27 -0400 Subject: [PATCH 5/7] actually include the command --- cmd/kots/cli/root.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/kots/cli/root.go b/cmd/kots/cli/root.go index 8035c5220a..555b9eb3ca 100644 --- a/cmd/kots/cli/root.go +++ b/cmd/kots/cli/root.go @@ -57,6 +57,7 @@ func RootCmd() *cobra.Command { cmd.AddCommand(EnableHACmd()) cmd.AddCommand(UpgradeServiceCmd()) cmd.AddCommand(AirgapUpdateCmd()) + cmd.AddCommand(EmbeddedClusterCmd()) viper.BindPFlags(cmd.Flags()) From 36fae1b138297d1d0fdd4c5809a38c475c5f4e8b Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Tue, 29 Oct 2024 19:20:18 -0400 Subject: [PATCH 6/7] f --- cmd/kots/cli/embedded-cluster-management.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/kots/cli/embedded-cluster-management.go b/cmd/kots/cli/embedded-cluster-management.go index 9a50da9b98..9154d176f5 100644 --- a/cmd/kots/cli/embedded-cluster-management.go +++ b/cmd/kots/cli/embedded-cluster-management.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "os" "time" cursor "github.com/ahmetalpbalkan/go-cursor" @@ -30,11 +29,6 @@ func EmbeddedClusterConfirmManagementCmd() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { v := viper.GetViper() - if len(args) < 1 { - cmd.Help() - os.Exit(1) - } - fmt.Print(cursor.Hide()) defer fmt.Print(cursor.Show()) From fe88fcae057ea86fca19b6d5be0339f28cc4dbc2 Mon Sep 17 00:00:00 2001 From: Andrew Lavery Date: Thu, 31 Oct 2024 10:36:08 -0400 Subject: [PATCH 7/7] return to 'deploy the app if --config-values are set in embedded cluster' --- cmd/kots/cli/embedded-cluster-management.go | 105 -------------------- cmd/kots/cli/embedded-cluster.go | 36 ------- cmd/kots/cli/root.go | 1 - pkg/airgap/airgap.go | 10 +- pkg/kotsadmconfig/config.go | 1 + pkg/online/online.go | 10 +- 6 files changed, 17 insertions(+), 146 deletions(-) delete mode 100644 cmd/kots/cli/embedded-cluster-management.go delete mode 100644 cmd/kots/cli/embedded-cluster.go diff --git a/cmd/kots/cli/embedded-cluster-management.go b/cmd/kots/cli/embedded-cluster-management.go deleted file mode 100644 index 9154d176f5..0000000000 --- a/cmd/kots/cli/embedded-cluster-management.go +++ /dev/null @@ -1,105 +0,0 @@ -package cli - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "time" - - cursor "github.com/ahmetalpbalkan/go-cursor" - "github.com/pkg/errors" - "github.com/replicatedhq/kots/pkg/auth" - "github.com/replicatedhq/kots/pkg/k8sutil" - "github.com/replicatedhq/kots/pkg/logger" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -func EmbeddedClusterConfirmManagementCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "confirm-management", - Short: "Confirm that the cluster is ready to deploy the application (that there are enough nodes, etc.)", - Long: "", - SilenceUsage: true, - SilenceErrors: false, - PreRun: func(cmd *cobra.Command, args []string) { - viper.BindPFlags(cmd.Flags()) - }, - RunE: func(cmd *cobra.Command, args []string) error { - v := viper.GetViper() - - fmt.Print(cursor.Hide()) - defer fmt.Print(cursor.Show()) - - log := logger.NewCLILogger(cmd.OutOrStdout()) - namespace, err := getNamespaceOrDefault(v.GetString("namespace")) - if err != nil { - return errors.Wrap(err, "failed to get namespace") - } - - clientset, err := k8sutil.GetClientset() - if err != nil { - return errors.Wrap(err, "failed to get clientset") - } - - getPodName := func() (string, error) { - return k8sutil.WaitForKotsadm(clientset, namespace, time.Second*5) - } - - stopCh := make(chan struct{}) - defer close(stopCh) - - log.ActionWithoutSpinner("Confirming cluster management...") - - localPort, errChan, err := k8sutil.PortForward(0, 3000, namespace, getPodName, false, stopCh, log) - if err != nil { - return errors.Wrap(err, "failed to start port forwarding") - } - - go func() { - select { - case err := <-errChan: - if err != nil { - log.Error(err) - } - case <-stopCh: - } - }() - - authSlug, err := auth.GetOrCreateAuthSlug(clientset, namespace) - if err != nil { - return errors.Wrap(err, "failed to get kotsadm auth slug") - } - - requestPayload := map[string]interface{}{} // empty payload - requestBody, err := json.Marshal(requestPayload) - if err != nil { - return errors.Wrap(err, "failed to marshal request json") - } - - url := fmt.Sprintf("http://localhost:%d/api/v1/embedded-cluster/management", localPort) - newRequest, err := http.NewRequest("POST", url, bytes.NewBuffer(requestBody)) - if err != nil { - return errors.Wrap(err, "failed to create http request") - } - newRequest.Header.Add("Authorization", authSlug) - newRequest.Header.Add("Content-Type", "application/json") - - resp, err := http.DefaultClient.Do(newRequest) - if err != nil { - return errors.Wrap(err, "failed to execute http request") - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("unexpected status code %d from %s", resp.StatusCode, url) - } - - log.ActionWithoutSpinner("Done") - - return nil - }, - } - return cmd -} diff --git a/cmd/kots/cli/embedded-cluster.go b/cmd/kots/cli/embedded-cluster.go deleted file mode 100644 index 5963233505..0000000000 --- a/cmd/kots/cli/embedded-cluster.go +++ /dev/null @@ -1,36 +0,0 @@ -package cli - -import ( - "os" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -func EmbeddedClusterCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "embedded-cluster", - Short: "Configure embedded-cluster resources", - Long: ``, - Hidden: true, - DisableFlagsInUseLine: true, // removes "kots set [flags]" from usage in help output - - SilenceUsage: true, - SilenceErrors: false, - PreRun: func(cmd *cobra.Command, args []string) { - viper.BindPFlags(cmd.Flags()) - }, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - cmd.Help() - os.Exit(1) - } - - return nil - }, - } - - cmd.AddCommand(EmbeddedClusterConfirmManagementCmd()) - - return cmd -} diff --git a/cmd/kots/cli/root.go b/cmd/kots/cli/root.go index 555b9eb3ca..8035c5220a 100644 --- a/cmd/kots/cli/root.go +++ b/cmd/kots/cli/root.go @@ -57,7 +57,6 @@ func RootCmd() *cobra.Command { cmd.AddCommand(EnableHACmd()) cmd.AddCommand(UpgradeServiceCmd()) cmd.AddCommand(AirgapUpdateCmd()) - cmd.AddCommand(EmbeddedClusterCmd()) viper.BindPFlags(cmd.Flags()) diff --git a/pkg/airgap/airgap.go b/pkg/airgap/airgap.go index 5600e0c9e5..7de2044ba4 100644 --- a/pkg/airgap/airgap.go +++ b/pkg/airgap/airgap.go @@ -281,8 +281,14 @@ func CreateAppFromAirgap(opts CreateAirgapAppOpts) (finalError error) { } if status == storetypes.VersionPendingClusterManagement { - // if pending cluster management, we don't want to deploy the app - return nil + if configFile != "" { + // if there is a config file, then we should proceed with the installation normally, not wait for the user + // to click through the UI to add nodes and configure the app + status = storetypes.VersionPendingConfig + } else { + // if pending cluster management, we don't want to deploy the app + return nil + } } hasStrictPreflights, err := store.GetStore().HasStrictPreflights(a.ID, newSequence) diff --git a/pkg/kotsadmconfig/config.go b/pkg/kotsadmconfig/config.go index cce6f780f8..100534ae3a 100644 --- a/pkg/kotsadmconfig/config.go +++ b/pkg/kotsadmconfig/config.go @@ -48,6 +48,7 @@ func IsUnsetItem(item kotsv1beta1.ConfigItem) bool { return true } +// NeedsConfiguration returns true if the app has required config values that are not set func NeedsConfiguration(appSlug string, sequence int64, isAirgap bool, kotsKinds *kotsutil.KotsKinds, registrySettings registrytypes.RegistrySettings) (bool, error) { log := logger.NewCLILogger(os.Stdout) diff --git a/pkg/online/online.go b/pkg/online/online.go index 0b707bcb27..7767874e45 100644 --- a/pkg/online/online.go +++ b/pkg/online/online.go @@ -220,8 +220,14 @@ func CreateAppFromOnline(opts CreateOnlineAppOpts) (_ *kotsutil.KotsKinds, final } if status == storetypes.VersionPendingClusterManagement { - // if pending cluster management, we don't want to deploy the app - return kotsKinds, nil + if configFile != "" { + // if there is a config file, then we should proceed with the installation normally, not wait for the user + // to click through the UI to add nodes and configure the app + status = storetypes.VersionPendingConfig + } else { + // if pending cluster management, we don't want to deploy the app + return kotsKinds, nil + } } hasStrictPreflights, err := store.GetStore().HasStrictPreflights(opts.PendingApp.ID, newSequence)