From e661a26493b555b32268d89ceb4d9b35859f83de Mon Sep 17 00:00:00 2001 From: nathan-nicholson Date: Tue, 28 Jan 2025 09:49:33 -0600 Subject: [PATCH] chore: more changes to cloud commands Signed-off-by: nathan-nicholson --- cmd/aws/command.go | 6 +- cmd/aws/create.go | 8 +- cmd/civo/command.go | 6 +- cmd/civo/create.go | 67 ++++++-- internal/cluster/cluster.go | 14 ++ internal/provision/provision.go | 240 ++++++++++++++++++++++++++- internal/provision/provision_test.go | 150 +++++++++++++++++ internal/step/stepper.go | 24 ++- internal/step/stepper_test.go | 2 +- 9 files changed, 490 insertions(+), 27 deletions(-) create mode 100644 internal/provision/provision_test.go diff --git a/cmd/aws/command.go b/cmd/aws/command.go index 888ccd9d5..ba737a02d 100644 --- a/cmd/aws/command.go +++ b/cmd/aws/command.go @@ -83,8 +83,10 @@ func Create() *cobra.Command { return fmt.Errorf("failed to get flags: %w", err) } + stepper := step.NewStepFactory(cmd.ErrOrStderr()) + // TODO: Handle for non-bubbletea - // progress.DisplayLogHints(40) + stepper.InfoStep(step.EMOJI_ALARM, fmt.Sprintf("Estimated time to complete: %s", "15-20 minutes")) isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) if !isValid { @@ -94,7 +96,7 @@ func Create() *cobra.Command { ctx := cmd.Context() k1Client := KubefirstAWSClient{ - stepper: step.NewStepFactory(cmd.ErrOrStderr()), + stepper: stepper, cliFlags: cliFlags, } diff --git a/cmd/aws/create.go b/cmd/aws/create.go index 90caa89b6..becc0b3b6 100644 --- a/cmd/aws/create.go +++ b/cmd/aws/create.go @@ -39,7 +39,7 @@ type KubefirstAWSClient struct { func (c *KubefirstAWSClient) CreateManagementCluster(ctx context.Context, catalogApps []apiTypes.GitopsCatalogApp) error { - initializeConfigStep := c.stepper.NewStep("Initialize Config") + initializeConfigStep := c.stepper.NewProgressStep("Initialize Config") cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(c.cliFlags.CloudRegion)) if err != nil { @@ -82,7 +82,7 @@ func (c *KubefirstAWSClient) CreateManagementCluster(ctx context.Context, catalo initializeConfigStep.Complete(nil) - validateGitStep := c.stepper.NewStep("Setup Gitops Repository") + validateGitStep := c.stepper.NewProgressStep("Setup Gitops Repository") gitAuth, err := gitShim.ValidateGitCredentials(c.cliFlags.GitProvider, c.cliFlags.GithubOrg, c.cliFlags.GitlabGroup) if err != nil { @@ -120,7 +120,7 @@ func (c *KubefirstAWSClient) CreateManagementCluster(ctx context.Context, catalo } validateGitStep.Complete(nil) - setupK3dClusterStep := c.stepper.NewStep("Setup k3d Cluster") + setupK3dClusterStep := c.stepper.NewProgressStep("Setup k3d Cluster") k3dClusterCreationComplete := viper.GetBool("launch.deployed") isK1Debug := strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" @@ -143,7 +143,7 @@ func (c *KubefirstAWSClient) CreateManagementCluster(ctx context.Context, catalo } setupK3dClusterStep.Complete(nil) - createMgmtClusterStep := c.stepper.NewStep("Create Management Cluster") + createMgmtClusterStep := c.stepper.NewProgressStep("Create Management Cluster") if err := provision.CreateMgmtCluster(gitAuth, c.cliFlags, catalogApps); err != nil { wrerr := fmt.Errorf("failed to create management cluster: %w", err) diff --git a/cmd/civo/command.go b/cmd/civo/command.go index 28a207682..22aa0dd47 100644 --- a/cmd/civo/command.go +++ b/cmd/civo/command.go @@ -64,6 +64,10 @@ func Create() *cobra.Command { return fmt.Errorf("failed to get CLI flags: %w", err) } + stepper := step.NewStepFactory(cmd.ErrOrStderr()) + + stepper.InfoStep(step.EMOJI_ALARM, fmt.Sprintf("Estimated time to complete: %s", "15-20 minutes")) + isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) if !isValid { return fmt.Errorf("catalog apps validation failed: %w", err) @@ -74,7 +78,7 @@ func Create() *cobra.Command { cliFlags: cliFlags, } - return k1Client.CreateCivoManagementCluster(cmd.Context(), catalogApps) + return k1Client.CreateManagementCluster(cmd.Context(), catalogApps) }, SilenceErrors: true, SilenceUsage: true, diff --git a/cmd/civo/create.go b/cmd/civo/create.go index 8ecd1e3ea..7d56030bc 100644 --- a/cmd/civo/create.go +++ b/cmd/civo/create.go @@ -11,6 +11,7 @@ import ( "fmt" "os" "strings" + "time" internalssh "github.com/konstructio/kubefirst-api/pkg/ssh" apiTypes "github.com/konstructio/kubefirst-api/pkg/types" @@ -31,20 +32,24 @@ type KubefirstCivoClient struct { cliFlags types.CliFlags } -func (kc *KubefirstCivoClient) CreateCivoManagementCluster(ctx context.Context, catalogApps []apiTypes.GitopsCatalogApp) error { - - initializeConfigStep := kc.stepper.NewStep("Initialize Config") +func (kc *KubefirstCivoClient) CreateManagementCluster(ctx context.Context, catalogApps []apiTypes.GitopsCatalogApp) error { err := ValidateProvidedFlags(kc.cliFlags.GitProvider, kc.cliFlags.DNSProvider) + if err != nil { - wrerr := fmt.Errorf("failed to validate provided flags: %w", err) - initializeConfigStep.Complete(wrerr) - return wrerr + return fmt.Errorf("failed to validate provided flags: %w", err) } - utilities.CreateK1ClusterDirectory(kc.cliFlags.ClusterName) + return CreateManagementCluster(kc, catalogApps) +} + +func CreateManagementCluster(c *KubefirstCivoClient, catalogApps []apiTypes.GitopsCatalogApp) error { + + initializeConfigStep := c.stepper.NewProgressStep("Initialize Config") - gitAuth, err := gitShim.ValidateGitCredentials(kc.cliFlags.GitProvider, kc.cliFlags.GithubOrg, kc.cliFlags.GitlabGroup) + utilities.CreateK1ClusterDirectory(c.cliFlags.ClusterName) + + gitAuth, err := gitShim.ValidateGitCredentials(c.cliFlags.GitProvider, c.cliFlags.GithubOrg, c.cliFlags.GitlabGroup) if err != nil { wrerr := fmt.Errorf("failed to validate git credentials: %w", err) initializeConfigStep.Complete(wrerr) @@ -52,16 +57,17 @@ func (kc *KubefirstCivoClient) CreateCivoManagementCluster(ctx context.Context, } initializeConfigStep.Complete(nil) - validateGitStep := kc.stepper.NewStep("Setup Gitops Repository") + validateGitStep := c.stepper.NewProgressStep("Setup Gitops Repository") // Validate git - executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", kc.cliFlags.GitProvider)) + executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", c.cliFlags.GitProvider)) + if !executionControl { newRepositoryNames := []string{"gitops", "metaphor"} newTeamNames := []string{"admins", "developers"} initGitParameters := gitShim.GitInitParameters{ - GitProvider: kc.cliFlags.GitProvider, + GitProvider: c.cliFlags.GitProvider, GitToken: gitAuth.Token, GitOwner: gitAuth.Owner, Repositories: newRepositoryNames, @@ -75,7 +81,11 @@ func (kc *KubefirstCivoClient) CreateCivoManagementCluster(ctx context.Context, return wrerr } } - viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", kc.cliFlags.GitProvider), true) + + viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", c.cliFlags.GitProvider), true) + + viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", c.cliFlags.GitProvider), true) + if err = viper.WriteConfig(); err != nil { wrerr := fmt.Errorf("failed to write viper config: %w", err) validateGitStep.Complete(wrerr) @@ -83,13 +93,14 @@ func (kc *KubefirstCivoClient) CreateCivoManagementCluster(ctx context.Context, } validateGitStep.Complete(nil) - setupK3dClusterStep := kc.stepper.NewStep("Setup k3d Cluster") + setupK3dClusterStep := c.stepper.NewProgressStep("Setup k3d Cluster") k3dClusterCreationComplete := viper.GetBool("launch.deployed") isK1Debug := strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" if !k3dClusterCreationComplete && !isK1Debug { - err = launch.Up(nil, true, kc.cliFlags.UseTelemetry) + err = launch.Up(nil, true, c.cliFlags.UseTelemetry) + if err != nil { wrerr := fmt.Errorf("failed to setup k3d cluster: %w", err) setupK3dClusterStep.Complete(wrerr) @@ -105,9 +116,9 @@ func (kc *KubefirstCivoClient) CreateCivoManagementCluster(ctx context.Context, } setupK3dClusterStep.Complete(nil) - createMgmtClusterStep := kc.stepper.NewStep("Create Management Cluster") + createMgmtClusterStep := c.stepper.NewProgressStep("Create Management Cluster") - if err := provision.CreateMgmtCluster(gitAuth, kc.cliFlags, catalogApps); err != nil { + if err := provision.CreateMgmtCluster(gitAuth, c.cliFlags, catalogApps); err != nil { wrerr := fmt.Errorf("failed to create management cluster: %w", err) createMgmtClusterStep.Complete(wrerr) return wrerr @@ -115,6 +126,30 @@ func (kc *KubefirstCivoClient) CreateCivoManagementCluster(ctx context.Context, createMgmtClusterStep.Complete(nil) + clusterClient := cluster.ClusterClient{} + + clusterProvision := provision.NewClusterProvision(c.cliFlags.ClusterName, &clusterClient) + + currentClusterStep := c.stepper.NewProgressStep(clusterProvision.GetCurrentStep()) + + for !clusterProvision.IsComplete() { + + if currentClusterStep.GetName() != clusterProvision.GetCurrentStep() { + currentClusterStep.Complete(nil) + currentClusterStep = c.stepper.NewProgressStep(clusterProvision.GetCurrentStep()) + } + + err = clusterProvision.UpdateProvisionProgress() + + if err != nil { + wrerr := fmt.Errorf("failure provisioning the management cluster: %w", err) + currentClusterStep.Complete(wrerr) + return wrerr + } + + time.Sleep(5 * time.Second) + } + return nil } diff --git a/internal/cluster/cluster.go b/internal/cluster/cluster.go index 02e2603da..6e8f8e340 100644 --- a/internal/cluster/cluster.go +++ b/internal/cluster/cluster.go @@ -29,6 +29,20 @@ func GetConsoleIngressURL() string { return "https://console.kubefirst.dev" } +type ClusterClient struct{} + +func (c *ClusterClient) CreateCluster(cluster apiTypes.ClusterDefinition) error { + return CreateCluster(cluster) +} + +func (c *ClusterClient) GetCluster(clusterName string) (apiTypes.Cluster, error) { + return GetCluster(clusterName) +} + +func (c *ClusterClient) ResetClusterProgress(clusterName string) error { + return ResetClusterProgress(clusterName) +} + func CreateCluster(cluster apiTypes.ClusterDefinition) error { customTransport := http.DefaultTransport.(*http.Transport).Clone() httpClient := http.Client{Transport: customTransport} diff --git a/internal/provision/provision.go b/internal/provision/provision.go index 2264e2493..7540db0ea 100644 --- a/internal/provision/provision.go +++ b/internal/provision/provision.go @@ -7,14 +7,21 @@ See the LICENSE file for more details. package provision import ( + "context" "errors" "fmt" + "os" + "strings" + "time" apiTypes "github.com/konstructio/kubefirst-api/pkg/types" "github.com/konstructio/kubefirst/internal/cluster" + "github.com/konstructio/kubefirst/internal/gitShim" + "github.com/konstructio/kubefirst/internal/launch" "github.com/konstructio/kubefirst/internal/types" "github.com/konstructio/kubefirst/internal/utilities" "github.com/rs/zerolog/log" + "github.com/spf13/viper" ) func CreateMgmtCluster(gitAuth apiTypes.GitAuth, cliFlags types.CliFlags, catalogApps []apiTypes.GitopsCatalogApp) error { @@ -31,7 +38,7 @@ func CreateMgmtCluster(gitAuth apiTypes.GitAuth, cliFlags types.CliFlags, catalo clusterCreated, err := cluster.GetCluster(clusterRecord.ClusterName) if err != nil && !errors.Is(err, cluster.ErrNotFound) { log.Printf("error retrieving cluster %q: %v", clusterRecord.ClusterName, err) - return fmt.Errorf("error retrieving cluster: %w", err) + return fmt.Errorf("error retrieving cluster %q: %w", clusterRecord.ClusterName, err) } if errors.Is(err, cluster.ErrNotFound) { @@ -49,3 +56,234 @@ func CreateMgmtCluster(gitAuth apiTypes.GitAuth, cliFlags types.CliFlags, catalo return nil } + +const ( + InstallToolsCheck = "Install Tools" + DomainLivenessCheck = "Domain Liveness" + KBotSetupCheck = "KBot Setup" + GitInitCheck = "Git Init" + GitOpsReadyCheck = "GitOps Ready" + GitTerraformApplyCheck = "Git Terraform Apply" + GitOpsPushedCheck = "GitOps Pushed" + CloudTerraformApplyCheck = "Cloud Terraform Apply" + ClusterSecretsCreatedCheck = "Cluster Secrets Created" + ArgoCDInstallCheck = "ArgoCD Install" + ArgoCDInitializeCheck = "ArgoCD Initialize" + VaultInitializedCheck = "Vault Initialized" + VaultTerraformApplyCheck = "Vault Terraform Apply" + UsersTerraformApplyCheck = "Users Terraform Apply" + ProvisionComplete = "Provision Complete" +) + +type ClusterClient interface { + GetCluster(clusterName string) (apiTypes.Cluster, error) + CreateCluster(cluster apiTypes.ClusterDefinition) error + ResetClusterProgress(clusterName string) error +} + +type ClusterProvision struct { + clusterName string + installSteps []installStep + client ClusterClient +} + +type installStep struct { + StepName string +} + +func NewClusterProvision(clusterName string, client ClusterClient) *ClusterProvision { + return &ClusterProvision{clusterName: clusterName, + installSteps: []installStep{ + {StepName: InstallToolsCheck}, + {StepName: DomainLivenessCheck}, + {StepName: KBotSetupCheck}, + {StepName: GitInitCheck}, + {StepName: GitOpsReadyCheck}, + {StepName: GitTerraformApplyCheck}, + {StepName: GitOpsPushedCheck}, + {StepName: CloudTerraformApplyCheck}, + {StepName: ClusterSecretsCreatedCheck}, + {StepName: ArgoCDInstallCheck}, + {StepName: ArgoCDInitializeCheck}, + {StepName: VaultInitializedCheck}, + {StepName: VaultTerraformApplyCheck}, + {StepName: UsersTerraformApplyCheck}, + }, + client: client, + } +} + +func (c *ClusterProvision) GetInstallSteps() []installStep { + return c.installSteps +} + +func (c *ClusterProvision) IsComplete() bool { + return len(c.installSteps) == 0 +} + +func (c *ClusterProvision) GetCurrentStep() string { + return c.installSteps[0].StepName +} + +func (c *ClusterProvision) PopStep() string { + if len(c.installSteps) == 0 { + return ProvisionComplete + } + + step := c.installSteps[0] + c.installSteps = c.installSteps[1:] + return step.StepName +} + +func (c *ClusterProvision) UpdateProvisionProgress() error { + provisionedCluster, err := c.client.GetCluster(c.clusterName) + if err != nil { + if errors.Is(err, cluster.ErrNotFound) { + return nil + } + + log.Printf("error retrieving cluster %q: %v", provisionedCluster.ClusterName, err) + return fmt.Errorf("error retrieving cluster %q: %w", provisionedCluster.ClusterName, err) + } + + if provisionedCluster.Status == "error" { + return fmt.Errorf("cluster in error state: %s", provisionedCluster.LastCondition) + } + + clusterStepStatus := map[string]bool{ + InstallToolsCheck: provisionedCluster.InstallToolsCheck, + DomainLivenessCheck: provisionedCluster.DomainLivenessCheck, + KBotSetupCheck: provisionedCluster.KbotSetupCheck, + GitInitCheck: provisionedCluster.GitInitCheck, + GitOpsReadyCheck: provisionedCluster.GitopsReadyCheck, + GitTerraformApplyCheck: provisionedCluster.GitTerraformApplyCheck, + GitOpsPushedCheck: provisionedCluster.GitopsPushedCheck, + CloudTerraformApplyCheck: provisionedCluster.CloudTerraformApplyCheck, + ClusterSecretsCreatedCheck: provisionedCluster.ClusterSecretsCreatedCheck, + ArgoCDInstallCheck: provisionedCluster.ArgoCDInstallCheck, + ArgoCDInitializeCheck: provisionedCluster.ArgoCDInitializeCheck, + VaultInitializedCheck: provisionedCluster.VaultInitializedCheck, + VaultTerraformApplyCheck: provisionedCluster.VaultTerraformApplyCheck, + UsersTerraformApplyCheck: provisionedCluster.UsersTerraformApplyCheck, + } + + if clusterStepStatus[c.GetCurrentStep()] { + c.PopStep() + } + + return nil +} + +type KubefirstClient interface { + CreateManagementCluster(ctx context.Context, catalogApps []apiTypes.GitopsCatalogApp) error +} + +func CreateManagementCluster(c KubefirstCivoClient, catalogApps []apiTypes.GitopsCatalogApp) error { + + initializeConfigStep := c.stepper.NewProgressStep("Initialize Config") + + utilities.CreateK1ClusterDirectory(c.cliFlags.ClusterName) + + gitAuth, err := gitShim.ValidateGitCredentials(c.cliFlags.GitProvider, c.cliFlags.GithubOrg, c.cliFlags.GitlabGroup) + if err != nil { + wrerr := fmt.Errorf("failed to validate git credentials: %w", err) + initializeConfigStep.Complete(wrerr) + return wrerr + } + + initializeConfigStep.Complete(nil) + validateGitStep := c.stepper.NewProgressStep("Setup Gitops Repository") + + // Validate git + executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", c.cliFlags.GitProvider)) + + if !executionControl { + newRepositoryNames := []string{"gitops", "metaphor"} + newTeamNames := []string{"admins", "developers"} + + initGitParameters := gitShim.GitInitParameters{ + GitProvider: c.cliFlags.GitProvider, + GitToken: gitAuth.Token, + GitOwner: gitAuth.Owner, + Repositories: newRepositoryNames, + Teams: newTeamNames, + } + + err = gitShim.InitializeGitProvider(&initGitParameters) + if err != nil { + wrerr := fmt.Errorf("failed to initialize Git provider: %w", err) + validateGitStep.Complete(wrerr) + return wrerr + } + } + + viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", c.cliFlags.GitProvider), true) + + viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", c.cliFlags.GitProvider), true) + + if err = viper.WriteConfig(); err != nil { + wrerr := fmt.Errorf("failed to write viper config: %w", err) + validateGitStep.Complete(wrerr) + return wrerr + } + + validateGitStep.Complete(nil) + setupK3dClusterStep := c.stepper.NewProgressStep("Setup k3d Cluster") + + k3dClusterCreationComplete := viper.GetBool("launch.deployed") + isK1Debug := strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" + + if !k3dClusterCreationComplete && !isK1Debug { + err = launch.Up(nil, true, c.cliFlags.UseTelemetry) + + if err != nil { + wrerr := fmt.Errorf("failed to setup k3d cluster: %w", err) + setupK3dClusterStep.Complete(wrerr) + return wrerr + } + } + + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api") + if err != nil { + wrerr := fmt.Errorf("API availability check failed: %w", err) + setupK3dClusterStep.Complete(wrerr) + return wrerr + } + + setupK3dClusterStep.Complete(nil) + createMgmtClusterStep := c.stepper.NewProgressStep("Create Management Cluster") + + if err := CreateMgmtCluster(gitAuth, c.cliFlags, catalogApps); err != nil { + wrerr := fmt.Errorf("failed to create management cluster: %w", err) + createMgmtClusterStep.Complete(wrerr) + return wrerr + } + + createMgmtClusterStep.Complete(nil) + + clusterClient := cluster.ClusterClient{} + + clusterProvision := NewClusterProvision(c.cliFlags.ClusterName, &clusterClient) + + currentClusterStep := c.stepper.NewProgressStep(clusterProvision.GetCurrentStep()) + + for !clusterProvision.IsComplete() { + + if currentClusterStep.GetName() != clusterProvision.GetCurrentStep() { + currentClusterStep.Complete(nil) + currentClusterStep = c.stepper.NewProgressStep(clusterProvision.GetCurrentStep()) + } + + err = clusterProvision.UpdateProvisionProgress() + + if err != nil { + wrerr := fmt.Errorf("failure provisioning the management cluster: %w", err) + currentClusterStep.Complete(wrerr) + return wrerr + } + + time.Sleep(5 * time.Second) + } + + return nil +} diff --git a/internal/provision/provision_test.go b/internal/provision/provision_test.go new file mode 100644 index 000000000..fb6260a00 --- /dev/null +++ b/internal/provision/provision_test.go @@ -0,0 +1,150 @@ +package provision + +import ( + "errors" + "fmt" + "strings" + "testing" + + "github.com/konstructio/kubefirst-api/pkg/types" + apiTypes "github.com/konstructio/kubefirst-api/pkg/types" +) + +func TestPopStep(t *testing.T) { + cp := NewClusterProvision("test-cluster") + initialLength := len(cp.installSteps) + + // Test first pop + firstStep := cp.installSteps[0].StepName + poppedStep := cp.PopStep() + + if poppedStep != firstStep { + t.Errorf("Expected popped step to be %s, got %s", firstStep, poppedStep) + } + + if len(cp.installSteps) != initialLength-1 { + t.Errorf("Expected length after pop to be %d, got %d", initialLength-1, len(cp.installSteps)) + } + + // Test that next step is now first + if cp.installSteps[0].StepName != "Domain Liveness" { + t.Errorf("Expected new first step to be 'Domain Liveness', got %s", cp.installSteps[0].StepName) + } + + // Pop all remaining steps + for i := 0; i < initialLength-1; i++ { + cp.PopStep() + } + +} + +func TestUpdateProvisionProgress(t *testing.T) { + t.Run("client returns error", func(t *testing.T) { + mockClient := &mockClusterClient{ + fnGetCluster: func(clusterName string) (*types.Cluster, error) { + return nil, errors.New("error getting cluster") + }, + } + + cp := NewClusterProvision("test-cluster") + cp.client = mockClient + + err := cp.UpdateProvisionProgress() + if err == nil { + t.Error("expected error but got none") + } + if !strings.Contains(err.Error(), "error getting cluster") { + t.Errorf("expected error containing 'error getting cluster', got %v", err) + } + }) + + t.Run("cluster in error state", func(t *testing.T) { + mockClient := &mockClusterClient{ + fnGetCluster: func(clusterName string) (*types.Cluster, error) { + return &types.Cluster{ + Status: "error", + LastCondition: "cluster in error state", + }, nil + }, + } + + cp := NewClusterProvision("test-cluster") + cp.client = mockClient + + err := cp.UpdateProvisionProgress() + if err == nil { + t.Error("expected error but got none") + } + if !strings.Contains(err.Error(), "cluster in error state") { + t.Errorf("expected error containing 'cluster in error state', got %v", err) + } + }) + + t.Run("successful progress update - step complete", func(t *testing.T) { + mockClient := &mockClusterClient{ + fnGetCluster: func(clusterName string) (*types.Cluster, error) { + return &types.Cluster{ + Status: "running", + InstallToolsCheck: true, + }, nil + }, + } + + cp := NewClusterProvision("test-cluster") + cp.client = mockClient + + err := cp.UpdateProvisionProgress() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if cp.GetCurrentStep() == InstallToolsCheck { + t.Error("step should have been popped but wasn't") + } + }) + + t.Run("successful progress update - step not complete", func(t *testing.T) { + mockClient := &mockClusterClient{ + fnGetCluster: func(clusterName string) (*types.Cluster, error) { + return &types.Cluster{ + Status: "running", + InstallToolsCheck: false, + }, nil + }, + } + + cp := NewClusterProvision("test-cluster") + cp.client = mockClient + + err := cp.UpdateProvisionProgress() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + }) +} + +type mockClusterClient struct { + fnGetCluster func(clusterName string) (*types.Cluster, error) + fnCreateCluster func(cluster apiTypes.ClusterDefinition) error + fnResetClusterProgress func(clusterName string) error +} + +func (m *mockClusterClient) GetCluster(clusterName string) (*types.Cluster, error) { + if m.fnGetCluster != nil { + return m.fnGetCluster(clusterName) + } + return nil, fmt.Errorf("not implemented") +} + +func (m *mockClusterClient) CreateCluster(cluster apiTypes.ClusterDefinition) error { + if m.fnCreateCluster != nil { + return m.fnCreateCluster(cluster) + } + return fmt.Errorf("not implemented") +} + +func (m *mockClusterClient) ResetClusterProgress(clusterName string) error { + if m.fnResetClusterProgress != nil { + return m.fnResetClusterProgress(clusterName) + } + return fmt.Errorf("not implemented") +} diff --git a/internal/step/stepper.go b/internal/step/stepper.go index 9bdcde976..9e71d1966 100644 --- a/internal/step/stepper.go +++ b/internal/step/stepper.go @@ -1,13 +1,29 @@ package step import ( + "fmt" "io" "github.com/konstructio/cli-utils/stepper" ) +const ( + EMOJI_CHECK = "✅" + EMOJI_ERROR = "🔴" + EMOJI_MAGIC = "✨" + EMOJI_HEAD = "🤕" + EMOJI_NO_ENTRY = "⛔" + EMOJI_TADA = "🎉" + EMOJI_ALARM = "⏰" + EMOJI_BUG = "🐛" + EMOJI_BULB = "💡" + EMOJI_WARNING = "⚠️" + EMOJI_WRENCH = "🔧" +) + type Stepper interface { - NewStep(stepName string) *stepper.Step + NewProgressStep(stepName string) *stepper.Step + InfoStep(emoji, message string) } type StepFactory struct { @@ -18,6 +34,10 @@ func NewStepFactory(writer io.Writer) *StepFactory { return &StepFactory{writer: writer} } -func (sf *StepFactory) NewStep(stepName string) *stepper.Step { +func (sf *StepFactory) NewProgressStep(stepName string) *stepper.Step { return stepper.New(sf.writer, stepName) } + +func (sf *StepFactory) InfoStep(emoji, message string) { + fmt.Fprintf(sf.writer, "%s %s\n", emoji, message) +} diff --git a/internal/step/stepper_test.go b/internal/step/stepper_test.go index 2f56fa2b7..5e2cf492b 100644 --- a/internal/step/stepper_test.go +++ b/internal/step/stepper_test.go @@ -23,7 +23,7 @@ func TestStepFactory_NewStep(t *testing.T) { buf := &bytes.Buffer{} sf := &StepFactory{writer: buf} - step := sf.NewStep(tt.stepName) + step := sf.NewProgressStep(tt.stepName) assert.NotNil(t, step) assert.Equal(t, tt.stepName, step.GetName())