From bb91e1c61f7ad5e7724b4ed0e102c072debdba2b Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:26:42 -0400 Subject: [PATCH 1/9] Clean up code using GolangCI-Lint --- .golangci.yaml | 121 +++++++++ cmd/akamai/command.go | 9 +- cmd/akamai/create.go | 27 +- cmd/aws/command.go | 6 +- cmd/aws/create.go | 32 +-- cmd/aws/quota.go | 34 +-- cmd/beta.go | 6 +- cmd/civo/backup.go | 12 +- cmd/civo/command.go | 68 ++--- cmd/civo/create.go | 23 +- cmd/civo/quota.go | 44 ++- cmd/digitalocean/command.go | 29 +- cmd/digitalocean/create.go | 27 +- cmd/google/command.go | 14 +- cmd/google/create.go | 26 +- cmd/info.go | 45 ++-- cmd/k3d/command.go | 8 +- cmd/k3d/create.go | 518 ++++++++++++------------------------ cmd/k3d/destroy.go | 134 ++++------ cmd/k3d/mkcert.go | 6 +- cmd/k3d/root-credentials.go | 17 +- cmd/k3d/vault.go | 60 ++--- cmd/k3s/command.go | 10 +- cmd/k3s/create.go | 30 ++- cmd/launch.go | 19 +- cmd/letsencrypt.go | 13 +- cmd/logs.go | 7 +- cmd/reset.go | 35 ++- cmd/root.go | 16 +- cmd/terraform.go | 1 + cmd/version.go | 4 +- cmd/vultr/command.go | 58 ++-- cmd/vultr/create.go | 35 +-- 33 files changed, 697 insertions(+), 797 deletions(-) create mode 100644 .golangci.yaml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 00000000..7d8269c1 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,121 @@ +run: + tests: false + concurrency: 5 + timeout: 5m + +linters: + disable-all: true + enable: + - gosimple + - govet + - ineffassign + - staticcheck + - unused + - asasalint + - asciicheck + - bidichk + - bodyclose + - contextcheck + - decorder + - dogsled + - dupl + - dupword + - durationcheck + - errchkjson + - errname + - errorlint + - exhaustive + - copyloopvar + - ginkgolinter + - gocheckcompilerdirectives + - gochecksumtype + - gocritic + - gocyclo + - gofmt + - gofumpt + - goheader + - goimports + - gomodguard + - goprintffuncname + - gosec + - gosmopolitan + - grouper + - importas + - inamedparam + - interfacebloat + - ireturn + - loggercheck + - makezero + - mirror + - misspell + - nakedret + - nilerr + - nilnil + - nonamedreturns + - nosprintfhostport + - paralleltest + - prealloc + - predeclared + - promlinter + - protogetter + - reassign + - revive + - rowserrcheck + - sloglint + - spancheck + - sqlclosecheck + - stylecheck + - tenv + - testableexamples + - testifylint + - testpackage + - thelper + - tparallel + - unconvert + - unparam + - usestdlibvars + - wastedassign + - whitespace + - wrapcheck + - zerologlint + +linters-settings: + perfsprint: + int-conversion: false + err-error: false + errorf: true + sprintf1: true + strconcat: false + + ireturn: + allow: + - anon + - error + - empty + - stdlib + - ssh.PublicKey + - tea.Model + + gosec: + confidence: medium + excludes: + - G107 # Potential HTTP request made with variable url: these are often false positives or intentional + - G110 # Decompression bombs: we can check these manually when submitting code + - G306 # Poor file permissions used when creating a directory: we can check these manually when submitting code + - G404 # Use of weak random number generator (math/rand instead of crypto/rand): we can live with these + + stylecheck: + checks: + - "all" + - "-ST1003" # this is covered by a different linter + + gocyclo: + min-complexity: 60 + + exhaustive: + check-generated: false + explicit-exhaustive-switch: false + explicit-exhaustive-map: false + default-case-required: false + default-signifies-exhaustive: true + package-scope-only: false diff --git a/cmd/akamai/command.go b/cmd/akamai/command.go index 82336e0b..167a8619 100644 --- a/cmd/akamai/command.go +++ b/cmd/akamai/command.go @@ -89,12 +89,12 @@ func Create() *cobra.Command { createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "the type of cluster to create (i.e. mgmt|workload)") createCmd.Flags().StringVar(&nodeCountFlag, "node-count", akamaiDefaults.NodeCount, "the node count for the cluster") createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", akamaiDefaults.InstanceSize, "the instance size of the cluster to create") - createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "cloudflare", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders)) + createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "cloudflare", fmt.Sprintf("the dns provider - one of: %q", supportedDNSProviders)) createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)") createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the DNS Name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") createCmd.MarkFlagRequired("domain-name") - createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) - createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "https", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %q", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "https", fmt.Sprintf("the git protocol - one of: %q", supportedGitProtocolOverride)) createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using github") createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "the branch to clone for the gitops-template repository") @@ -112,7 +112,6 @@ func Destroy() *cobra.Command { Short: "destroy the kubefirst platform", Long: "destroy the kubefirst platform running in akamai and remove all resources", RunE: common.Destroy, - // PreRun: common.CheckDocker, } return destroyCmd @@ -126,7 +125,7 @@ func RootCredentials() *cobra.Command { RunE: common.GetRootCredentials, } - authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)") + authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the ArgoCD password to the clipboard (optional)") authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)") authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)") diff --git a/cmd/akamai/create.go b/cmd/akamai/create.go index ed0177ce..b4e23a51 100644 --- a/cmd/akamai/create.go +++ b/cmd/akamai/create.go @@ -29,33 +29,30 @@ func createAkamai(cmd *cobra.Command, args []string) error { cliFlags, err := utilities.GetFlags(cmd, "akamai") if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to get flags: %w", err) } progress.DisplayLogHints(25) isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) if !isValid { - return err + return fmt.Errorf("catalog validation failed: %w", err) } err = ValidateProvidedFlags(cliFlags.GitProvider) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate flags: %w", err) } - // If cluster setup is complete, return - utilities.CreateK1ClusterDirectory(clusterNameFlag) gitAuth, err := gitShim.ValidateGitCredentials(cliFlags.GitProvider, cliFlags.GithubOrg, cliFlags.GitlabGroup) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate git credentials: %w", err) } - // Validate git executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider)) if !executionControl { newRepositoryNames := []string{"gitops", "metaphor"} @@ -72,7 +69,7 @@ func createAkamai(cmd *cobra.Command, args []string) error { err = gitShim.InitializeGitProvider(&initGitParameters) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to initialize git provider: %w", err) } } viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider), true) @@ -88,10 +85,10 @@ func createAkamai(cmd *cobra.Command, args []string) error { err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") + return fmt.Errorf("kubefirst api is unavailable: %w", err) } provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) - return nil } @@ -99,11 +96,9 @@ func ValidateProvidedFlags(gitProvider string) error { progress.AddStep("Validate provided flags") if os.Getenv("LINODE_TOKEN") == "" { - // telemetryShim.Transmit(useTelemetryFlag, segmentClient, segment.MetricCloudCredentialsCheckFailed, "LINODE_TOKEN environment variable was not set") return fmt.Errorf("your LINODE_TOKEN is not set - please set and re-run your last command") } - // Validate required environment variables for dns provider if dnsProviderFlag == "cloudflare" { if os.Getenv("CF_API_TOKEN") == "" { return fmt.Errorf("your CF_API_TOKEN environment variable is not set. Please set and try again") @@ -114,17 +109,15 @@ func ValidateProvidedFlags(gitProvider string) error { case "github": key, err := internalssh.GetHostKey("github.com") if err != nil { - return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "github.com", key.Type()) + return fmt.Errorf("failed to fetch github host key: %w", err) } + log.Info().Msgf("%q %s", "github.com", key.Type()) case "gitlab": key, err := internalssh.GetHostKey("gitlab.com") if err != nil { - return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) + return fmt.Errorf("failed to fetch gitlab host key: %w", err) } + log.Info().Msgf("%q %s", "gitlab.com", key.Type()) } progress.CompleteStep("Validate provided flags") diff --git a/cmd/aws/command.go b/cmd/aws/command.go index 77fffcd2..bdf80129 100644 --- a/cmd/aws/command.go +++ b/cmd/aws/command.go @@ -85,12 +85,12 @@ func Create() *cobra.Command { createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "the type of cluster to create (i.e. mgmt|workload)") createCmd.Flags().StringVar(&nodeCountFlag, "node-count", awsDefaults.NodeCount, "the node count for the cluster") createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", awsDefaults.InstanceSize, "the instance size of the cluster to create") - createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "aws", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders)) + createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "aws", fmt.Sprintf("the dns provider - one of: %q", supportedDNSProviders)) createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)") createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the Route53/Cloudflare hosted zone name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") createCmd.MarkFlagRequired("domain-name") - createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) - createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %q", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %q", supportedGitProtocolOverride)) createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using github") createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "the branch to clone for the gitops-template repository") diff --git a/cmd/aws/create.go b/cmd/aws/create.go index e2e51a2b..4c9a00ef 100644 --- a/cmd/aws/create.go +++ b/cmd/aws/create.go @@ -38,13 +38,13 @@ func createAws(cmd *cobra.Command, args []string) error { isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) if !isValid { - return err + return fmt.Errorf("invalid catalog apps: %w", err) } err = ValidateProvidedFlags(cliFlags.GitProvider) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate provided flags: %w", err) } utilities.CreateK1ClusterDirectory(cliFlags.ClusterName) @@ -64,24 +64,26 @@ func createAws(cmd *cobra.Command, args []string) error { creds, err := awsClient.Config.Credentials.Retrieve(aws.BackgroundContext()) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to retrieve AWS credentials: %w", err) } viper.Set("kubefirst.state-store-creds.access-key-id", creds.AccessKeyID) viper.Set("kubefirst.state-store-creds.secret-access-key-id", creds.SecretAccessKey) viper.Set("kubefirst.state-store-creds.token", creds.SessionToken) - viper.WriteConfig() + if err := viper.WriteConfig(); err != nil { + return fmt.Errorf("failed to write config: %w", err) + } _, err = awsClient.CheckAvailabilityZones(cliFlags.CloudRegion) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to check availability zones: %w", err) } gitAuth, err := gitShim.ValidateGitCredentials(cliFlags.GitProvider, cliFlags.GithubOrg, cliFlags.GitlabGroup) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate Git credentials: %w", err) } executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider)) @@ -100,12 +102,14 @@ func createAws(cmd *cobra.Command, args []string) error { err = gitShim.InitializeGitProvider(&initGitParameters) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to initialize Git provider: %w", err) } } viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider), true) - viper.WriteConfig() + if err := viper.WriteConfig(); err != nil { + return fmt.Errorf("failed to write config: %w", err) + } k3dClusterCreationComplete := viper.GetBool("launch.deployed") isK1Debug := strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" @@ -117,10 +121,10 @@ func createAws(cmd *cobra.Command, args []string) error { err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") + return fmt.Errorf("failed to check kubefirst API availability: %w", err) } provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) - return nil } @@ -138,17 +142,15 @@ func ValidateProvidedFlags(gitProvider string) error { case "github": key, err := internalssh.GetHostKey("github.com") if err != nil { - return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "github.com", key.Type()) + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy: %w", err) } + log.Info().Msgf("%q %s", "github.com", key.Type()) case "gitlab": key, err := internalssh.GetHostKey("gitlab.com") if err != nil { - return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy: %w", err) } + log.Info().Msgf("%q %s", "gitlab.com", key.Type()) } progress.CompleteStep("Validate provided flags") diff --git a/cmd/aws/quota.go b/cmd/aws/quota.go index d54f50d0..b5b69ced 100644 --- a/cmd/aws/quota.go +++ b/cmd/aws/quota.go @@ -18,30 +18,33 @@ import ( // printAwsQuotaWarning provides visual output detailing quota health for aws func printAwsQuotaWarning(messageHeader string, output map[string][]awsinternal.QuotaDetailResponse) string { - var createAwsQuotaWarning bytes.Buffer - createAwsQuotaWarning.WriteString(strings.Repeat("-", 70)) - createAwsQuotaWarning.WriteString(fmt.Sprintf("\n%s\n", messageHeader)) - createAwsQuotaWarning.WriteString(strings.Repeat("-", 70)) - createAwsQuotaWarning.WriteString("\n") + var buf bytes.Buffer + + fmt.Fprintln(&buf, strings.Repeat("-", 70)) + fmt.Fprintln(&buf, messageHeader) + fmt.Fprintln(&buf, strings.Repeat("-", 70)) + fmt.Fprintln(&buf, "") + for service, quotas := range output { - createAwsQuotaWarning.WriteString(fmt.Sprintf("%s\n", service)) - createAwsQuotaWarning.WriteString(strings.Repeat("-", 35)) - createAwsQuotaWarning.WriteString("\n") + fmt.Fprintln(&buf, service) + fmt.Fprintln(&buf, strings.Repeat("-", 35)) + fmt.Fprintln(&buf, "") + for _, thing := range quotas { - createAwsQuotaWarning.WriteString(fmt.Sprintf("%s: %v\n", thing.QuotaName, thing.QuotaValue)) + fmt.Fprintf(&buf, "%s: %v\n", thing.QuotaName, thing.QuotaValue) } - createAwsQuotaWarning.WriteString("\n") + fmt.Fprintln(&buf, "") } // Write to logs, but also output to stdout - return createAwsQuotaWarning.String() + return buf.String() } // evalAwsQuota provides an interface to the command-line -func evalAwsQuota(cmd *cobra.Command, args []string) error { +func evalAwsQuota(cmd *cobra.Command, _ []string) error { cloudRegionFlag, err := cmd.Flags().GetString("cloud-region") if err != nil { - return err + return fmt.Errorf("failed to get cloud region flag: %w", err) } awsClient := &awsinternal.AWSConfiguration{ @@ -49,17 +52,16 @@ func evalAwsQuota(cmd *cobra.Command, args []string) error { } quotaDetails, err := awsClient.GetServiceQuotas([]string{"eks", "vpc"}) if err != nil { - return err + return fmt.Errorf("failed to get service quotas: %w", err) } messageHeader := fmt.Sprintf( - "AWS Quota Health\nRegion: %s\n\nIf you encounter issues deploying your kubefirst cluster, check these quotas and determine if you need to request a limit increase.", + "AWS Quota Health\nRegion: %s\n\nIf you encounter issues deploying your Kubefirst cluster, check these quotas and determine if you need to request a limit increase.", cloudRegionFlag, ) result := printAwsQuotaWarning(messageHeader, quotaDetails) // Write to logs, but also output to stdout fmt.Println(reports.StyleMessage(result)) - return nil } diff --git a/cmd/beta.go b/cmd/beta.go index 777c0cb6..395311da 100644 --- a/cmd/beta.go +++ b/cmd/beta.go @@ -20,10 +20,10 @@ import ( // betaCmd represents the beta command tree var betaCmd = &cobra.Command{ Use: "beta", - Short: "access kubefirst beta features", - Long: `access kubefirst beta features`, + Short: "access Kubefirst beta features", + Long: `access Kubefirst beta features`, Run: func(cmd *cobra.Command, args []string) { - fmt.Println("To learn more about kubefirst, run:") + fmt.Println("To learn more about Kubefirst, run:") fmt.Println(" kubefirst help") if progress.Progress != nil { diff --git a/cmd/civo/backup.go b/cmd/civo/backup.go index 8985e1b2..74d37e68 100644 --- a/cmd/civo/backup.go +++ b/cmd/civo/backup.go @@ -7,6 +7,7 @@ See the LICENSE file for more details. package civo import ( + "fmt" "os" "github.com/konstructio/kubefirst-api/pkg/providerConfigs" @@ -17,7 +18,7 @@ import ( "github.com/spf13/viper" ) -func backupCivoSSL(cmd *cobra.Command, args []string) error { +func backupCivoSSL(cmd *cobra.Command, _ []string) error { utils.DisplayLogHints() clusterName := viper.GetString("flags.cluster-name") @@ -33,7 +34,7 @@ func backupCivoSSL(cmd *cobra.Command, args []string) error { case "gitlab": cGitOwner = viper.GetString("flags.gitlab-owner") default: - log.Panic().Msgf("invalid git provider option") + return fmt.Errorf("invalid git provider option: %q", gitProvider) } config := providerConfigs.GetConfig( @@ -52,7 +53,7 @@ func backupCivoSSL(cmd *cobra.Command, args []string) error { for _, path := range paths { if _, err := os.Stat(path); os.IsNotExist(err) { - log.Info().Msgf("checking path: %s", path) + log.Info().Msgf("checking path: %q", path) err := os.MkdirAll(path, os.ModePerm) if err != nil { log.Info().Msg("directory already exists, continuing") @@ -61,10 +62,9 @@ func backupCivoSSL(cmd *cobra.Command, args []string) error { } } - err := ssl.Backup(config.SSLBackupDir, domainNameFlag, config.K1Dir, config.Kubeconfig) + err := ssl.Backup(config.SSLBackupDir, domainName, config.K1Dir, config.Kubeconfig) if err != nil { - log.Info().Msg("error backing up ssl resources") - return err + return fmt.Errorf("error backing up SSL resources: %w", err) } return nil } diff --git a/cmd/civo/command.go b/cmd/civo/command.go index 4bda3c65..1c09088a 100644 --- a/cmd/civo/command.go +++ b/cmd/civo/command.go @@ -52,10 +52,10 @@ var ( func NewCommand() *cobra.Command { civoCmd := &cobra.Command{ Use: "civo", - Short: "kubefirst civo installation", - Long: "kubefirst civo", - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("To learn more about civo in kubefirst, run:") + Short: "Kubefirst Civo installation", + Long: "Kubefirst Civo", + Run: func(cmd *cobra.Command, _ []string) { + fmt.Println("To learn more about Civo in Kubefirst, run:") fmt.Println(" kubefirst civo --help") if progress.Progress != nil { @@ -73,8 +73,8 @@ func NewCommand() *cobra.Command { func BackupSSL() *cobra.Command { backupSSLCmd := &cobra.Command{ Use: "backup-ssl", - Short: "backup the cluster resources related tls certificates from cert-manager", - Long: "kubefirst uses a combination of external-dns, ingress-nginx, and cert-manager for provisioning automated tls certificates for services with an ingress. this command will backup all the kubernetes resources to restore in a new cluster with the same domain name", + Short: "Backup the cluster resources related to TLS certificates from cert-manager", + Long: "Kubefirst uses a combination of external-dns, ingress-nginx, and cert-manager for provisioning automated TLS certificates for services with an ingress. This command will backup all the Kubernetes resources to restore in a new cluster with the same domain name", RunE: backupCivoSSL, } @@ -84,7 +84,7 @@ func BackupSSL() *cobra.Command { func Create() *cobra.Command { createCmd := &cobra.Command{ Use: "create", - Short: "create the kubefirst platform running on civo kubernetes", + Short: "Create the Kubefirst platform running on Civo Kubernetes", TraverseChildren: true, RunE: createCivo, // PreRun: common.CheckDocker, @@ -93,27 +93,27 @@ func Create() *cobra.Command { civoDefaults := constants.GetCloudDefaults().Civo // todo review defaults and update descriptions - createCmd.Flags().StringVar(&alertsEmailFlag, "alerts-email", "", "email address for let's encrypt certificate notifications (required)") + createCmd.Flags().StringVar(&alertsEmailFlag, "alerts-email", "", "Email address for Let's Encrypt certificate notifications (required)") createCmd.MarkFlagRequired("alerts-email") - createCmd.Flags().BoolVar(&ciFlag, "ci", false, "if running kubefirst in ci, set this flag to disable interactive features") - createCmd.Flags().StringVar(&cloudRegionFlag, "cloud-region", "NYC1", "the civo region to provision infrastructure in") - createCmd.Flags().StringVar(&clusterNameFlag, "cluster-name", "kubefirst", "the name of the cluster to create") - createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "the type of cluster to create (i.e. mgmt|workload)") - createCmd.Flags().StringVar(&nodeCountFlag, "node-count", civoDefaults.NodeCount, "the node count for the cluster") - createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", civoDefaults.InstanceSize, "the instance size of the cluster to create") - createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "civo", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders)) - createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)") - createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the Civo DNS Name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") + createCmd.Flags().BoolVar(&ciFlag, "ci", false, "If running Kubefirst in CI, set this flag to disable interactive features") + createCmd.Flags().StringVar(&cloudRegionFlag, "cloud-region", "NYC1", "The Civo region to provision infrastructure in") + createCmd.Flags().StringVar(&clusterNameFlag, "cluster-name", "kubefirst", "The name of the cluster to create") + createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "The type of cluster to create (i.e. mgmt|workload)") + createCmd.Flags().StringVar(&nodeCountFlag, "node-count", civoDefaults.NodeCount, "The node count for the cluster") + createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", civoDefaults.InstanceSize, "The instance size of the cluster to create") + createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "civo", fmt.Sprintf("The DNS provider - one of: %s", supportedDNSProviders)) + createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "The subdomain to use for DNS records (Cloudflare)") + createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "The Civo DNS Name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") createCmd.MarkFlagRequired("domain-name") - createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) - createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) - createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using github") - createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") - createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "the branch to clone for the gitops-template repository") - createCmd.Flags().StringVar(&gitopsTemplateURLFlag, "gitops-template-url", "https://github.com/konstructio/gitops-template.git", "the fully qualified url to the gitops-template repository to clone") - createCmd.Flags().StringVar(&installCatalogApps, "install-catalog-apps", "", "comma separated values to install after provision") - createCmd.Flags().BoolVar(&useTelemetryFlag, "use-telemetry", true, "whether to emit telemetry") - createCmd.Flags().BoolVar(&installKubefirstProFlag, "install-kubefirst-pro", true, "whether or not to install kubefirst pro") + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("The git provider - one of: %s", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("The git protocol - one of: %s", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "The GitHub organization for the new GitOps and Metaphor repositories - required if using GitHub") + createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "The GitLab group for the new GitOps and Metaphor projects - required if using GitLab") + createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "The branch to clone for the gitops-template repository") + createCmd.Flags().StringVar(&gitopsTemplateURLFlag, "gitops-template-url", "https://github.com/konstructio/gitops-template.git", "The fully qualified URL to the gitops-template repository to clone") + createCmd.Flags().StringVar(&installCatalogApps, "install-catalog-apps", "", "Comma separated values to install after provision") + createCmd.Flags().BoolVar(&useTelemetryFlag, "use-telemetry", true, "Whether to emit telemetry") + createCmd.Flags().BoolVar(&installKubefirstProFlag, "install-kubefirst-pro", true, "Whether or not to install Kubefirst Pro") return createCmd } @@ -121,8 +121,8 @@ func Create() *cobra.Command { func Destroy() *cobra.Command { destroyCmd := &cobra.Command{ Use: "destroy", - Short: "destroy the kubefirst platform", - Long: "destroy the kubefirst platform running in civo and remove all resources", + Short: "Destroy the Kubefirst platform", + Long: "Destroy the Kubefirst platform running in Civo and remove all resources", RunE: common.Destroy, // PreRun: common.CheckDocker, } @@ -138,7 +138,7 @@ func Quota() *cobra.Command { RunE: evalCivoQuota, } - quotaCmd.Flags().StringVar(&cloudRegionFlag, "cloud-region", "NYC1", "the civo region to monitor quotas in") + quotaCmd.Flags().StringVar(&cloudRegionFlag, "cloud-region", "NYC1", "The Civo region to monitor quotas in") return quotaCmd } @@ -146,14 +146,14 @@ func Quota() *cobra.Command { func RootCredentials() *cobra.Command { authCmd := &cobra.Command{ Use: "root-credentials", - Short: "retrieve root authentication information for platform components", - Long: "retrieve root authentication information for platform components", + Short: "Retrieve root authentication information for platform components", + Long: "Retrieve root authentication information for platform components", RunE: common.GetRootCredentials, } - authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)") - authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)") - authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)") + authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "Copy the ArgoCD password to the clipboard (optional)") + authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "Copy the Kbot password to the clipboard (optional)") + authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "Copy the Vault password to the clipboard (optional)") return authCmd } diff --git a/cmd/civo/create.go b/cmd/civo/create.go index f1f36bef..d59699ee 100644 --- a/cmd/civo/create.go +++ b/cmd/civo/create.go @@ -29,20 +29,20 @@ func createCivo(cmd *cobra.Command, args []string) error { cliFlags, err := utilities.GetFlags(cmd, "civo") if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to get CLI flags: %w", err) } progress.DisplayLogHints(15) isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) if !isValid { - return err + return fmt.Errorf("catalog apps validation failed: %w", err) } err = ValidateProvidedFlags(cliFlags.GitProvider) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate provided flags: %w", err) } // If cluster setup is complete, return @@ -52,7 +52,7 @@ func createCivo(cmd *cobra.Command, args []string) error { gitAuth, err := gitShim.ValidateGitCredentials(cliFlags.GitProvider, cliFlags.GithubOrg, cliFlags.GitlabGroup) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate git credentials: %w", err) } // Validate git @@ -72,11 +72,13 @@ func createCivo(cmd *cobra.Command, args []string) error { err = gitShim.InitializeGitProvider(&initGitParameters) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to initialize Git provider: %w", err) } } viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider), true) - viper.WriteConfig() + if err = viper.WriteConfig(); err != nil { + return fmt.Errorf("failed to write viper config: %w", err) + } k3dClusterCreationComplete := viper.GetBool("launch.deployed") isK1Debug := strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" @@ -88,10 +90,10 @@ func createCivo(cmd *cobra.Command, args []string) error { err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") + return fmt.Errorf("API availability check failed: %w", err) } provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) - return nil } @@ -99,7 +101,6 @@ func ValidateProvidedFlags(gitProvider string) error { progress.AddStep("Validate provided flags") if os.Getenv("CIVO_TOKEN") == "" { - // telemetryShim.Transmit(useTelemetryFlag, segmentClient, segment.MetricCloudCredentialsCheckFailed, "CIVO_TOKEN environment variable was not set") return fmt.Errorf("your CIVO_TOKEN is not set - please set and re-run your last command") } @@ -115,16 +116,14 @@ func ValidateProvidedFlags(gitProvider string) error { key, err := internalssh.GetHostKey("github.com") if err != nil { return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "github.com", key.Type()) } + log.Info().Msgf("github.com %q", key.Type()) case "gitlab": key, err := internalssh.GetHostKey("gitlab.com") if err != nil { return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) } + log.Info().Msgf("gitlab.com %q", key.Type()) } progress.CompleteStep("Validate provided flags") diff --git a/cmd/civo/quota.go b/cmd/civo/quota.go index a4f5eef3..a5d129d6 100644 --- a/cmd/civo/quota.go +++ b/cmd/civo/quota.go @@ -23,19 +23,19 @@ import ( ) const ( - // The threshold at which civo quotas will trigger a warning + // The threshold at which Civo quotas will trigger a warning quotaObjectThresholdWarning = 80 - // The threshold at which civo quotas will trigger a critical warning + // The threshold at which Civo quotas will trigger a critical warning quotaObjectThresholdCritical = 90 // The link to request a quota limit increase within Civo civoQuotaIncreaseLink = "https://dashboard.civo.com/quota/edit" ) -// checkFields asserts which limits should be checked against the civo quota +// checkFields asserts which limits should be checked against the Civo quota // These fields are the json values of the Quota struct // https://github.com/civo/civogo/blob/master/quota.go#L9 // All values used here must be of type int, excluding the string fields -var checkFields map[string]string = map[string]string{ +var checkFields = map[string]string{ "cpu_core_limit": "cpu_core_usage", "database_count_limit": "database_count_usage", "database_cpu_core_limit": "database_cpu_core_usage", @@ -68,20 +68,19 @@ type quotaFormattedOutput struct { limitValue float64 } -// returnCivoQuotaEvaluation fetches quota from civo and compares limits to usage +// returnCivoQuotaEvaluation fetches quota from Civo and compares limits to usage func returnCivoQuotaEvaluation(cloudRegion string) (string, int, int, error) { - // Fetch quota from civo + // Fetch quota from Civo client, err := civogo.NewClient(os.Getenv("CIVO_TOKEN"), cloudRegion) if err != nil { - log.Info().Msg(err.Error()) - return "", 0, 0, err + log.Printf("failed to create Civo client: %v", err) + return "", 0, 0, fmt.Errorf("failed to create Civo client: %w", err) } quota, err := client.GetQuota() if err != nil { - log.Info().Msgf("failed to fetch civo quota: %s", err) - return "", 0, 0, err - + log.Printf("failed to fetch Civo quota: %v", err) + return "", 0, 0, fmt.Errorf("failed to fetch Civo quota: %w", err) } // Container for quota response as a map @@ -90,13 +89,12 @@ func returnCivoQuotaEvaluation(cloudRegion string) (string, int, int, error) { // Marshal quota and unmarshal into map quotaJSON, err := json.Marshal(quota) if err != nil { - log.Info().Msgf("failed to marshal civo quota struct: %s", err) - return "", 0, 0, err + log.Printf("failed to marshal Civo quota struct: %v", err) + return "", 0, 0, fmt.Errorf("failed to marshal Civo quota struct: %w", err) } - err = json.Unmarshal(quotaJSON, "aMap) - if err != nil { - log.Info().Msgf("failed to unmarshal civo quota struct: %s", err) - return "", 0, 0, err + if err := json.Unmarshal(quotaJSON, "aMap); err != nil { + log.Printf("failed to unmarshal Civo quota struct: %v", err) + return "", 0, 0, fmt.Errorf("failed to unmarshal Civo quota struct: %w", err) } // Compare actual to limit and warn against threshold @@ -118,11 +116,11 @@ func returnCivoQuotaEvaluation(cloudRegion string) (string, int, int, error) { switch { case percentCalc > quotaObjectThresholdWarning && percentCalc < quotaObjectThresholdCritical: - quotaWarnings += 1 + quotaWarnings++ outputFormat := checkObj.formatQuotaOutput(yellow(percentExpr)) output = append(output, outputFormat) case percentCalc > quotaObjectThresholdCritical: - quotaFailures += 1 + quotaFailures++ outputFormat := checkObj.formatQuotaOutput(red(percentExpr)) output = append(output, outputFormat) default: @@ -149,7 +147,7 @@ func (q quotaFormattedOutput) formatQuotaOutput(usageExpression string) string { ) } -// printCivoQuotaWarning provides visual output detailing quota health for civo +// printCivoQuotaWarning provides visual output detailing quota health for Civo func printCivoQuotaWarning(messageHeader string, output []string) string { var createCivoQuotaWarning bytes.Buffer createCivoQuotaWarning.WriteString(strings.Repeat("-", 70)) @@ -170,17 +168,17 @@ func printCivoQuotaWarning(messageHeader string, output []string) string { func evalCivoQuota(cmd *cobra.Command, args []string) error { civoToken := os.Getenv("CIVO_TOKEN") if len(civoToken) == 0 { - return fmt.Errorf("\n\nYour CIVO_TOKEN environment variable isn't set,\nvisit this link https://dashboard.civo.com/security and set CIVO_TOKEN") + return fmt.Errorf("your CIVO_TOKEN environment variable isn't set, visit this link https://dashboard.civo.com/security and set CIVO_TOKEN") } cloudRegionFlag, err := cmd.Flags().GetString("cloud-region") if err != nil { - return err + return fmt.Errorf("failed to get cloud region flag: %w", err) } message, _, _, err := returnCivoQuotaEvaluation(cloudRegionFlag) if err != nil { - return err + return fmt.Errorf("failed to evaluate Civo quota: %w", err) } // Write to logs, but also output to stdout diff --git a/cmd/digitalocean/command.go b/cmd/digitalocean/command.go index e04ce26d..3ab8a3f0 100644 --- a/cmd/digitalocean/command.go +++ b/cmd/digitalocean/command.go @@ -12,7 +12,6 @@ import ( "github.com/konstructio/kubefirst-api/pkg/constants" "github.com/konstructio/kubefirst/internal/common" "github.com/konstructio/kubefirst/internal/progress" - "github.com/spf13/cobra" ) @@ -53,10 +52,10 @@ var ( func NewCommand() *cobra.Command { digitaloceanCmd := &cobra.Command{ Use: "digitalocean", - Short: "kubefirst DigitalOcean installation", - Long: "kubefirst digitalocean", + Short: "Kubefirst DigitalOcean installation", + Long: "Kubefirst DigitalOcean", Run: func(cmd *cobra.Command, args []string) { - fmt.Println("To learn more about digital ocean in kubefirst, run:") + fmt.Println("To learn more about DigitalOcean in Kubefirst, run:") fmt.Println(" kubefirst digitalocean --help") if progress.Progress != nil { @@ -77,7 +76,7 @@ func NewCommand() *cobra.Command { func Create() *cobra.Command { createCmd := &cobra.Command{ Use: "create", - Short: "create the kubefirst platform running on DigitalOcean Kubernetes", + Short: "create the Kubefirst platform running on DigitalOcean Kubernetes", TraverseChildren: true, RunE: createDigitalocean, // PreRun: common.CheckDocker, @@ -88,25 +87,25 @@ func Create() *cobra.Command { // todo review defaults and update descriptions createCmd.Flags().StringVar(&alertsEmailFlag, "alerts-email", "", "email address for let's encrypt certificate notifications (required)") createCmd.MarkFlagRequired("alerts-email") - createCmd.Flags().BoolVar(&ciFlag, "ci", false, "if running kubefirst in ci, set this flag to disable interactive features") + createCmd.Flags().BoolVar(&ciFlag, "ci", false, "if running Kubefirst in CI, set this flag to disable interactive features") createCmd.Flags().StringVar(&cloudRegionFlag, "cloud-region", "nyc3", "the DigitalOcean region to provision infrastructure in") createCmd.Flags().StringVar(&clusterNameFlag, "cluster-name", "kubefirst", "the name of the cluster to create") createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "the type of cluster to create (i.e. mgmt|workload)") createCmd.Flags().StringVar(&nodeCountFlag, "node-count", doDefaults.NodeCount, "the node count for the cluster") createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", doDefaults.InstanceSize, "the instance size of the cluster to create") - createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "digitalocean", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders)) + createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "digitalocean", fmt.Sprintf("the dns provider - one of: %q", supportedDNSProviders)) createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)") createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the DigitalOcean DNS Name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") createCmd.MarkFlagRequired("domain-name") - createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) - createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) - createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using github") - createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %q", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %q", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using GitHub") + createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using GitLab") createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "the branch to clone for the gitops-template repository") createCmd.Flags().StringVar(&gitopsTemplateURLFlag, "gitops-template-url", "https://github.com/konstructio/gitops-template.git", "the fully qualified url to the gitops-template repository to clone") createCmd.Flags().StringVar(&installCatalogApps, "install-catalog-apps", "", "comma separated values to install after provision") createCmd.Flags().BoolVar(&useTelemetryFlag, "use-telemetry", true, "whether to emit telemetry") - createCmd.Flags().BoolVar(&installKubefirstProFlag, "install-kubefirst-pro", true, "whether or not to install kubefirst pro") + createCmd.Flags().BoolVar(&installKubefirstProFlag, "install-kubefirst-pro", true, "whether or not to install Kubefirst Pro") return createCmd } @@ -114,8 +113,8 @@ func Create() *cobra.Command { func Destroy() *cobra.Command { destroyCmd := &cobra.Command{ Use: "destroy", - Short: "destroy the kubefirst platform", - Long: "destroy the kubefirst platform running in DigitalOcean and remove all resources", + Short: "destroy the Kubefirst platform", + Long: "destroy the Kubefirst platform running in DigitalOcean and remove all resources", RunE: common.Destroy, // PreRun: common.CheckDocker, } @@ -131,7 +130,7 @@ func RootCredentials() *cobra.Command { RunE: common.GetRootCredentials, } - authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)") + authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the ArgoCD password to the clipboard (optional)") authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)") authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)") diff --git a/cmd/digitalocean/create.go b/cmd/digitalocean/create.go index 9ae9e2aa..3c705b0f 100644 --- a/cmd/digitalocean/create.go +++ b/cmd/digitalocean/create.go @@ -7,6 +7,7 @@ See the LICENSE file for more details. package digitalocean import ( + "errors" "fmt" "os" "strings" @@ -29,20 +30,24 @@ func createDigitalocean(cmd *cobra.Command, args []string) error { cliFlags, err := utilities.GetFlags(cmd, "digitalocean") if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to get CLI flags: %w", err) } progress.DisplayLogHints(20) isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) + if err != nil { + return fmt.Errorf("catalog validation error: %w", err) + } + if !isValid { - return err + return errors.New("catalog did not pass a validation check") } err = ValidateProvidedFlags(cliFlags.GitProvider) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate provided flags: %w", err) } // If cluster setup is complete, return @@ -50,7 +55,7 @@ func createDigitalocean(cmd *cobra.Command, args []string) error { if clusterSetupComplete { err = fmt.Errorf("this cluster install process has already completed successfully") progress.Error(err.Error()) - return nil + return err } utilities.CreateK1ClusterDirectory(clusterNameFlag) @@ -58,7 +63,7 @@ func createDigitalocean(cmd *cobra.Command, args []string) error { gitAuth, err := gitShim.ValidateGitCredentials(cliFlags.GitProvider, cliFlags.GithubOrg, cliFlags.GitlabGroup) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate git credentials: %w", err) } // Validate git @@ -78,7 +83,7 @@ func createDigitalocean(cmd *cobra.Command, args []string) error { err = gitShim.InitializeGitProvider(&initGitParameters) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to initialize git provider: %w", err) } } viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider), true) @@ -94,10 +99,10 @@ func createDigitalocean(cmd *cobra.Command, args []string) error { err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") + return fmt.Errorf("failed to check app availability for Kubefirst API: %w", err) } provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) - return nil } @@ -113,7 +118,7 @@ func ValidateProvidedFlags(gitProvider string) error { for _, env := range []string{"DO_TOKEN", "DO_SPACES_KEY", "DO_SPACES_SECRET"} { if os.Getenv(env) == "" { - return fmt.Errorf("your %s variable is unset - please set it before continuing", env) + return fmt.Errorf("your %q variable is unset - please set it before continuing", env) } } @@ -122,16 +127,14 @@ func ValidateProvidedFlags(gitProvider string) error { key, err := internalssh.GetHostKey("github.com") if err != nil { return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "github.com", key.Type()) } + log.Info().Msgf("%q %s", "github.com", key.Type()) case "gitlab": key, err := internalssh.GetHostKey("gitlab.com") if err != nil { return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) } + log.Info().Msgf("%q %s", "gitlab.com", key.Type()) } progress.CompleteStep("Validate provided flags") diff --git a/cmd/google/command.go b/cmd/google/command.go index e53ac885..009e0e30 100644 --- a/cmd/google/command.go +++ b/cmd/google/command.go @@ -57,7 +57,7 @@ func NewCommand() *cobra.Command { Use: "google", Short: "kubefirst Google installation", Long: "kubefirst google", - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { fmt.Println("To learn more about google in kubefirst, run:") fmt.Println(" kubefirst beta google --help") @@ -79,7 +79,7 @@ func NewCommand() *cobra.Command { func Create() *cobra.Command { createCmd := &cobra.Command{ Use: "create", - Short: "create the kubefirst platform running on GCP kubernetes", + Short: "create the kubefirst platform running on GCP Kubernetes", TraverseChildren: true, RunE: createGoogle, // PreRun: common.CheckDocker, @@ -96,14 +96,14 @@ func Create() *cobra.Command { createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "the type of cluster to create (i.e. mgmt|workload)") createCmd.Flags().StringVar(&nodeCountFlag, "node-count", googleDefaults.NodeCount, "the node count for the cluster") createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", googleDefaults.InstanceSize, "the instance size of the cluster to create") - createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "google", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders)) + createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "google", fmt.Sprintf("the dns provider - one of: %q", supportedDNSProviders)) createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)") createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the GCP DNS Name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") createCmd.MarkFlagRequired("domain-name") createCmd.Flags().StringVar(&googleProjectFlag, "google-project", "", "google project id (required)") createCmd.MarkFlagRequired("google-project") - createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) - createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %q", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %q", supportedGitProtocolOverride)) createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using github") createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "the branch to clone for the gitops-template repository") @@ -120,7 +120,7 @@ func Destroy() *cobra.Command { destroyCmd := &cobra.Command{ Use: "destroy", Short: "destroy the kubefirst platform", - Long: "destroy the kubefirst platform running in Goole and remove all resources", + Long: "destroy the kubefirst platform running in Google and remove all resources", RunE: common.Destroy, // PreRun: common.CheckDocker, } @@ -136,7 +136,7 @@ func RootCredentials() *cobra.Command { RunE: common.GetRootCredentials, } - authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)") + authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the ArgoCD password to the clipboard (optional)") authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)") authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)") diff --git a/cmd/google/create.go b/cmd/google/create.go index e15651df..087eb928 100644 --- a/cmd/google/create.go +++ b/cmd/google/create.go @@ -30,28 +30,27 @@ func createGoogle(cmd *cobra.Command, args []string) error { cliFlags, err := utilities.GetFlags(cmd, "google") if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to get flags: %w", err) } progress.DisplayLogHints(20) isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) if !isValid { - return err + return fmt.Errorf("catalog apps validation failed: %w", err) } err = ValidateProvidedFlags(cliFlags.GitProvider) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("validation of provided flags failed: %w", err) } - // If cluster setup is complete, return clusterSetupComplete := viper.GetBool("kubefirst-checks.cluster-install-complete") if clusterSetupComplete { err = fmt.Errorf("this cluster install process has already completed successfully") progress.Error(err.Error()) - return nil + return fmt.Errorf("cluster setup is complete: %w", err) } utilities.CreateK1ClusterDirectory(clusterNameFlag) @@ -59,7 +58,7 @@ func createGoogle(cmd *cobra.Command, args []string) error { gitAuth, err := gitShim.ValidateGitCredentials(cliFlags.GitProvider, cliFlags.GithubOrg, cliFlags.GitlabGroup) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("git credentials validation failed: %w", err) } executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider)) @@ -77,7 +76,7 @@ func createGoogle(cmd *cobra.Command, args []string) error { err = gitShim.InitializeGitProvider(&initGitParameters) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("initialization of git provider failed: %w", err) } } viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider), true) @@ -93,10 +92,10 @@ func createGoogle(cmd *cobra.Command, args []string) error { err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") + return fmt.Errorf("kubefirst api availability check failed: %w", err) } provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) - return nil } @@ -110,23 +109,22 @@ func ValidateProvidedFlags(gitProvider string) error { _, err := os.Open(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")) if err != nil { progress.Error("Unable to read GOOGLE_APPLICATION_CREDENTIALS file") + return fmt.Errorf("could not open GOOGLE_APPLICATION_CREDENTIALS file: %w", err) } switch gitProvider { case "github": key, err := internalssh.GetHostKey("github.com") if err != nil { - return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "github.com", key.Type()) + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy: %w", err) } + log.Info().Msgf("%q %s", "github.com", key.Type()) case "gitlab": key, err := internalssh.GetHostKey("gitlab.com") if err != nil { - return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy: %w", err) } + log.Info().Msgf("%q %s", "gitlab.com", key.Type()) } progress.CompleteStep("Validate provided flags") diff --git a/cmd/info.go b/cmd/info.go index f1a5b116..5b6cc2e4 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -7,7 +7,10 @@ See the LICENSE file for more details. package cmd import ( + "bytes" + "fmt" "runtime" + "text/tabwriter" "github.com/konstructio/kubefirst-api/pkg/configs" "github.com/konstructio/kubefirst/internal/progress" @@ -22,30 +25,24 @@ var infoCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { config := configs.ReadConfig() - content := ` -## -# Info summary - -| Name | Value | -| --- | --- | -| Operational System | ` + config.LocalOs + ` | -| Architecture | ` + config.LocalArchitecture + `| -| Go Lang version | ` + runtime.Version() + `| -| Kubefirst config file | ` + config.KubefirstConfigFilePath + `| -| Kubefirst config folder | ` + config.K1FolderPath + `| -| Kubefirst Version | ` + configs.K1Version + `| -` - - // infoSummary.WriteString(fmt.Sprintf("Kubectl path: %s", config.KubectlClientPath)) - // infoSummary.WriteString(fmt.Sprintf("Terraform path: %s", config.TerraformClientPath)) - // infoSummary.WriteString(fmt.Sprintf("Kubeconfig path: %s", config.KubeConfigPath)) - - // if configs.K1Version == "" { - // infoSummary.WriteString("\n\nWarning: It seems you are running kubefirst in development mode,") - // infoSummary.WriteString(" please use LDFLAGS to ensure you use the proper template version and avoid unexpected behavior") - // } - - progress.Success(content) + var buf bytes.Buffer + + tw := tabwriter.NewWriter(&buf, 0, 0, 1, ' ', tabwriter.Debug) + + fmt.Fprintln(&buf, "##") + fmt.Fprintln(&buf, "# Info summary") + fmt.Fprintln(&buf, "") + + fmt.Fprintf(tw, "Name\tValue\n") + fmt.Fprintf(tw, "---\t---\n") + fmt.Fprintf(tw, "Operational System\t%s\n", config.LocalOs) + fmt.Fprintf(tw, "Architecture\t%s\n", config.LocalArchitecture) + fmt.Fprintf(tw, "Golang version\t%s\n", runtime.Version()) + fmt.Fprintf(tw, "Kubefirst config file\t%s\n", config.KubefirstConfigFilePath) + fmt.Fprintf(tw, "Kubefirst config folder\t%s\n", config.K1FolderPath) + fmt.Fprintf(tw, "Kubefirst Version\t%s\n", configs.K1Version) + + progress.Success(buf.String()) }, } diff --git a/cmd/k3d/command.go b/cmd/k3d/command.go index ed9cde81..13bc7422 100644 --- a/cmd/k3d/command.go +++ b/cmd/k3d/command.go @@ -39,7 +39,7 @@ var ( // Supported git providers supportedGitProviders = []string{"github", "gitlab"} - // Supported git providers + // Supported git protocols supportedGitProtocolOverride = []string{"https", "ssh"} ) @@ -89,8 +89,8 @@ func Create() *cobra.Command { createCmd.Flags().BoolVar(&ciFlag, "ci", false, "if running kubefirst in ci, set this flag to disable interactive features") createCmd.Flags().StringVar(&clusterNameFlag, "cluster-name", "kubefirst", "the name of the cluster to create") createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "the type of cluster to create (i.e. mgmt|workload)") - createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) - createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %q", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %q", supportedGitProtocolOverride)) createCmd.Flags().StringVar(&githubUserFlag, "github-user", "", "the GitHub user for the new gitops and metaphor repositories - this cannot be used with --github-org") createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - this cannot be used with --github-user") createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") @@ -137,7 +137,7 @@ func RootCredentials() *cobra.Command { RunE: getK3dRootCredentials, } - authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)") + authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the ArgoCD password to the clipboard (optional)") authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)") authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)") diff --git a/cmd/k3d/create.go b/cmd/k3d/create.go index dd60016a..5eb6df74 100644 --- a/cmd/k3d/create.go +++ b/cmd/k3d/create.go @@ -18,19 +18,17 @@ import ( "syscall" "time" + argocdapi "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" "github.com/atotto/clipboard" "github.com/dustin/go-humanize" - "github.com/rs/zerolog/log" - - argocdapi "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" "github.com/go-git/go-git/v5" githttps "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/konstructio/kubefirst-api/pkg/argocd" "github.com/konstructio/kubefirst-api/pkg/configs" constants "github.com/konstructio/kubefirst-api/pkg/constants" "github.com/konstructio/kubefirst-api/pkg/gitClient" - github "github.com/konstructio/kubefirst-api/pkg/github" - gitlab "github.com/konstructio/kubefirst-api/pkg/gitlab" + "github.com/konstructio/kubefirst-api/pkg/github" + "github.com/konstructio/kubefirst-api/pkg/gitlab" "github.com/konstructio/kubefirst-api/pkg/handlers" "github.com/konstructio/kubefirst-api/pkg/k3d" "github.com/konstructio/kubefirst-api/pkg/k8s" @@ -50,6 +48,7 @@ import ( "github.com/kubefirst/metrics-client/pkg/telemetry" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -60,70 +59,64 @@ import ( func runK3d(cmd *cobra.Command, args []string) error { ciFlag, err := cmd.Flags().GetBool("ci") if err != nil { - return err + return fmt.Errorf("failed to get 'ci' flag: %w", err) } clusterNameFlag, err := cmd.Flags().GetString("cluster-name") if err != nil { - return err + return fmt.Errorf("failed to get 'cluster-name' flag: %w", err) } clusterTypeFlag, err := cmd.Flags().GetString("cluster-type") if err != nil { - return err + return fmt.Errorf("failed to get 'cluster-type' flag: %w", err) } githubOrgFlag, err := cmd.Flags().GetString("github-org") if err != nil { - return err + return fmt.Errorf("failed to get 'github-org' flag: %w", err) } githubUserFlag, err := cmd.Flags().GetString("github-user") if err != nil { - return err + return fmt.Errorf("failed to get 'github-user' flag: %w", err) } gitlabGroupFlag, err := cmd.Flags().GetString("gitlab-group") if err != nil { - return err + return fmt.Errorf("failed to get 'gitlab-group' flag: %w", err) } gitProviderFlag, err := cmd.Flags().GetString("git-provider") if err != nil { - return err + return fmt.Errorf("failed to get 'git-provider' flag: %w", err) } gitProtocolFlag, err := cmd.Flags().GetString("git-protocol") if err != nil { - return err + return fmt.Errorf("failed to get 'git-protocol' flag: %w", err) } gitopsTemplateURLFlag, err := cmd.Flags().GetString("gitops-template-url") if err != nil { - return err + return fmt.Errorf("failed to get 'gitops-template-url' flag: %w", err) } gitopsTemplateBranchFlag, err := cmd.Flags().GetString("gitops-template-branch") if err != nil { - return err + return fmt.Errorf("failed to get 'gitops-template-branch' flag: %w", err) } installCatalogAppsFlag, err := cmd.Flags().GetString("install-catalog-apps") if err != nil { - return err + return fmt.Errorf("failed to get 'install-catalog-apps' flag: %w", err) } useTelemetryFlag, err := cmd.Flags().GetBool("use-telemetry") if err != nil { - return err + return fmt.Errorf("failed to get 'use-telemetry' flag: %w", err) } - // // If cluster setup is complete, return - // clusterSetupComplete := viper.GetBool("kubefirst-checks.cluster-install-complete") - // if clusterSetupComplete { - // return fmt.Errorf("this cluster install process has already completed successfully") - // } - utilities.CreateK1ClusterDirectory(clusterNameFlag) utils.DisplayLogHints() @@ -136,44 +129,29 @@ func runK3d(cmd *cobra.Command, args []string) error { case "github": key, err := internalssh.GetHostKey("github.com") if err != nil { - return errors.New("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "github.com", key.Type()) + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy: %w", err) } + log.Info().Msgf("Host key for github.com: %q", key.Type()) case "gitlab": key, err := internalssh.GetHostKey("gitlab.com") if err != nil { - return errors.New("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy: %w", err) } + log.Info().Msgf("Host key for gitlab.com: %q", key.Type()) } - // Either user or org can be specified for github, not both if githubOrgFlag != "" && githubUserFlag != "" { return errors.New("only one of --github-user or --github-org can be supplied") } - // Check for existing port forwards before continuing err = k8s.CheckForExistingPortForwards(8080, 8200, 9000, 9094) if err != nil { - return fmt.Errorf("%s - this port is required to set up your kubefirst environment - please close any existing port forwards before continuing", err.Error()) + return fmt.Errorf("error checking existing port forwards: %w", err) } - // Verify Docker is running # TODO: reintroduce once we support more runtimes - // dcli := docker.DockerClientWrapper{ - // Client: docker.NewDockerClient(), - // } - // _, err = dcli.CheckDockerReady() - // if err != nil { - // return err - // } - - // Global context ctx, cancel := context.WithCancel(context.Background()) defer cancel() - // Clients httpClient := http.DefaultClient kubefirstTeam := os.Getenv("KUBEFIRST_TEAM") @@ -181,7 +159,6 @@ func runK3d(cmd *cobra.Command, args []string) error { kubefirstTeam = "false" } - // Store flags for application state maintenance viper.Set("flags.cluster-name", clusterNameFlag) viper.Set("flags.domain-name", k3d.DomainName) viper.Set("flags.git-provider", gitProviderFlag) @@ -189,7 +166,6 @@ func runK3d(cmd *cobra.Command, args []string) error { viper.Set("kubefirst.cloud-provider", "k3d") viper.WriteConfig() - // Switch based on git provider, set params var cGitHost, cGitOwner, cGitUser, cGitToken, containerRegistryHost string var cGitlabOwnerGroupID int switch gitProviderFlag { @@ -197,43 +173,36 @@ func runK3d(cmd *cobra.Command, args []string) error { cGitHost = k3d.GithubHost containerRegistryHost = "ghcr.io" - // Attempt to retrieve session-scoped token for GitHub user gitHubService := services.NewGitHubService(httpClient) gitHubHandler := handlers.NewGitHubHandler(gitHubService) - // var existingToken string if os.Getenv("GITHUB_TOKEN") != "" { existingToken = os.Getenv("GITHUB_TOKEN") - } else if os.Getenv("GITHUB_TOKEN") == "" && viper.GetString("github.session_token") != "" { + } else if viper.GetString("github.session_token") != "" { existingToken = viper.GetString("github.session_token") } gitHubAccessToken, err := wrappers.AuthenticateGitHubUserWrapper(existingToken, gitHubHandler) if err != nil { - log.Warn().Msgf(err.Error()) + log.Warn().Msg(err.Error()) } - // Token will either be user-provided or generated by kubefirst invocation cGitToken = gitHubAccessToken - // Verify token scopes err = github.VerifyTokenPermissions(cGitToken) if err != nil { - return err + return fmt.Errorf("failed to verify GitHub token permissions: %w", err) } - log.Info().Msg("verifying github authentication") + log.Info().Msg("verifying GitHub authentication") githubUser, err := gitHubHandler.GetGitHubUser(cGitToken) if err != nil { - return err + return fmt.Errorf("failed to get GitHub user: %w", err) } - // Owner is either an organization or a personal user's GitHub handle if githubOrgFlag != "" { cGitOwner = githubOrgFlag - } else if githubUserFlag != "" { - cGitOwner = githubUser - } else if githubOrgFlag == "" && githubUserFlag == "" { + } else { cGitOwner = githubUser } cGitUser = githubUser @@ -243,85 +212,54 @@ func runK3d(cmd *cobra.Command, args []string) error { viper.WriteConfig() case "gitlab": if gitlabGroupFlag == "" { - return fmt.Errorf("please provide a gitlab group using the --gitlab-group flag") - } - - if os.Getenv("GITLAB_TOKEN") == "" { - return fmt.Errorf("GITLAB_TOKEN environment variable unset - please set it and try again") + return errors.New("please provide a gitlab group using the --gitlab-group flag") } cGitToken = os.Getenv("GITLAB_TOKEN") + if cGitToken == "" { + return errors.New("GITLAB_TOKEN environment variable unset - please set it and try again") + } - // Verify token scopes err = gitlab.VerifyTokenPermissions(cGitToken) if err != nil { - return err + return fmt.Errorf("failed to verify GitLab token permissions: %w", err) } gitlabClient, err := gitlab.NewGitLabClient(cGitToken, gitlabGroupFlag) if err != nil { - return err + return fmt.Errorf("failed to create GitLab client: %w", err) } cGitHost = k3d.GitlabHost cGitOwner = gitlabClient.ParentGroupPath cGitlabOwnerGroupID = gitlabClient.ParentGroupID - log.Info().Msgf("set gitlab owner to %s", cGitOwner) + log.Info().Msgf("set gitlab owner to %q", cGitOwner) - // Get authenticated user's name user, _, err := gitlabClient.Client.Users.CurrentUser() if err != nil { - return fmt.Errorf("unable to get authenticated user info - please make sure GITLAB_TOKEN env var is set %s", err.Error()) + return fmt.Errorf("unable to get authenticated user info - please make sure GITLAB_TOKEN env var is set: %w", err) } cGitUser = user.Username - - containerRegistryHost = "registry.gitlab.com" viper.Set("flags.gitlab-owner", gitlabGroupFlag) viper.Set("flags.gitlab-owner-group-id", cGitlabOwnerGroupID) viper.Set("gitlab.session_token", cGitToken) viper.WriteConfig() default: - log.Error().Msgf("invalid git provider option") + return fmt.Errorf("invalid git provider option %q", gitProviderFlag) } - // Ask for confirmation var gitDestDescriptor string switch gitProviderFlag { case "github": if githubOrgFlag != "" { gitDestDescriptor = "Organization" - } - if githubUserFlag != "" { - gitDestDescriptor = "User" - } - if githubUserFlag == "" && githubOrgFlag == "" { + } else { gitDestDescriptor = "User" } case "gitlab": gitDestDescriptor = "Group" } - // todo - // Since it's possible to stop and restart, cGitOwner may need to be reset - //if cGitOwner == "" { - // switch gitProviderFlag { - // case "github": - // cGitOwner = viper.GetString("flags.github-owner") - // case "gitlab": - // cGitOwner = viper.GetString("flags.gitlab-owner") - // } - //} - // - //model, err := presentRecap(gitProviderFlag, gitDestDescriptor, cGitOwner) - //if err != nil { - // return err - //} - //_, err = tea.NewProgram(model).Run() - //if err != nil { - // return err - //} - - // Instantiate K3d config config := k3d.GetConfig(clusterNameFlag, gitProviderFlag, cGitOwner, gitProtocolFlag) switch gitProviderFlag { case "github": @@ -342,13 +280,10 @@ func runK3d(cmd *cobra.Command, args []string) error { segClient := segment.InitClient(clusterId, clusterTypeFlag, gitProviderFlag) - // Progress output progressPrinter.AddTracker("preflight-checks", "Running preflight checks", 5) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) progressPrinter.IncrementTracker("preflight-checks", 1) - // this branch flag value is overridden with a tag when running from a - // kubefirst binary for version compatibility switch configs.K1Version { case "development": if strings.Contains(gitopsTemplateURLFlag, "https://github.com/konstructio/gitops-template.git") && gitopsTemplateBranchFlag == "" { @@ -356,24 +291,24 @@ func runK3d(cmd *cobra.Command, args []string) error { } default: switch gitopsTemplateURLFlag { - case "https://github.com/konstructio/gitops-template.git": // default value + case "https://github.com/konstructio/gitops-template.git": if gitopsTemplateBranchFlag == "" { gitopsTemplateBranchFlag = configs.K1Version } - case "https://github.com/konstructio/gitops-template": // edge case for valid but incomplete url + case "https://github.com/konstructio/gitops-template": if gitopsTemplateBranchFlag == "" { gitopsTemplateBranchFlag = configs.K1Version } - default: // not equal to our defaults - if gitopsTemplateBranchFlag == "" { // didn't supply the branch flag but they did supply the repo flag - return fmt.Errorf("must supply gitops-template-branch flag when gitops-template-url is overridden") + default: + if gitopsTemplateBranchFlag == "" { + return errors.New("must supply gitops-template-branch flag when gitops-template-url is overridden") } } } - log.Info().Msgf("kubefirst version configs.K1Version: %s ", configs.K1Version) - log.Info().Msgf("cloning gitops-template repo url: %s ", gitopsTemplateURLFlag) - log.Info().Msgf("cloning gitops-template repo branch: %s ", gitopsTemplateBranchFlag) + log.Info().Msgf("kubefirst version configs.K1Version: %q", configs.K1Version) + log.Info().Msgf("cloning gitops-template repo url: %q", gitopsTemplateURLFlag) + log.Info().Msgf("cloning gitops-template repo branch: %q", gitopsTemplateBranchFlag) atlantisWebhookSecret := viper.GetString("secrets.atlantis-webhook") if atlantisWebhookSecret == "" { @@ -391,13 +326,11 @@ func runK3d(cmd *cobra.Command, args []string) error { log.Info().Msg("checking authentication to required providers") - // check disk free, err := utils.GetAvailableDiskSize() if err != nil { - return err + return fmt.Errorf("failed to get available disk size: %w", err) } - // convert available disk size to GB format availableDiskSize := float64(free) / humanize.GByte if availableDiskSize < constants.MinimumAvailableDiskSize { return fmt.Errorf( @@ -407,20 +340,14 @@ func runK3d(cmd *cobra.Command, args []string) error { } progressPrinter.IncrementTracker("preflight-checks", 1) - // Objects to check for - // Repositories that will be created throughout the initialization process newRepositoryNames := []string{"gitops", "metaphor"} newTeamNames := []string{"admins", "developers"} - // Check git credentials executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", config.GitProvider)) if !executionControl { telemetry.SendEvent(segClient, telemetry.GitCredentialsCheckStarted, "") if len(cGitToken) == 0 { - msg := fmt.Sprintf( - "please set a %s_TOKEN environment variable to continue", - strings.ToUpper(config.GitProvider), - ) + msg := fmt.Sprintf("please set a %s_TOKEN environment variable to continue", strings.ToUpper(config.GitProvider)) telemetry.SendEvent(segClient, telemetry.GitCredentialsCheckFailed, msg) return errors.New(msg) } @@ -434,7 +361,7 @@ func runK3d(cmd *cobra.Command, args []string) error { } err = gitShim.InitializeGitProvider(&initGitParameters) if err != nil { - return err + return fmt.Errorf("failed to initialize Git provider: %w", err) } viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", config.GitProvider), true) @@ -442,10 +369,10 @@ func runK3d(cmd *cobra.Command, args []string) error { telemetry.SendEvent(segClient, telemetry.GitCredentialsCheckCompleted, "") progressPrinter.IncrementTracker("preflight-checks", 1) } else { - log.Info().Msg(fmt.Sprintf("already completed %s checks - continuing", config.GitProvider)) + log.Info().Msg(fmt.Sprintf("already completed %q checks - continuing", config.GitProvider)) progressPrinter.IncrementTracker("preflight-checks", 1) } - // Swap tokens for git protocol + var gitopsRepoURL string executionControl = viper.GetBool("kubefirst-checks.kbot-setup") if !executionControl { @@ -455,7 +382,7 @@ func runK3d(cmd *cobra.Command, args []string) error { sshPrivateKey, sshPublicKey, err = utils.CreateSshKeyPair() if err != nil { telemetry.SendEvent(segClient, telemetry.KbotSetupFailed, err.Error()) - return err + return fmt.Errorf("failed to create SSH key pair: %w", err) } log.Info().Msg("ssh key pair creation complete") @@ -474,10 +401,8 @@ func runK3d(cmd *cobra.Command, args []string) error { log.Info().Msg("validation and kubefirst cli environment check is complete") - telemetry.SendEvent(segClient, telemetry.InitCompleted, "") telemetry.SendEvent(segClient, telemetry.InitCompleted, "") - // Swap tokens for git protocol switch config.GitProtocol { case "https": gitopsRepoURL = config.DestinationGitopsRepoURL @@ -520,23 +445,22 @@ func runK3d(cmd *cobra.Command, args []string) error { gitopsDirectoryTokens.UseTelemetry = "false" } - //* generate http credentials for git auth over https httpAuth := &githttps.BasicAuth{ Username: cGitUser, Password: cGitToken, } if err != nil { - log.Info().Msgf("generate public keys failed: %s\n", err.Error()) + log.Info().Msgf("generate public keys failed: %q", err.Error()) + return fmt.Errorf("failed to generate public keys: %w", err) } - //* download dependencies to `$HOME/.k1/tools` if !viper.GetBool("kubefirst-checks.tools-downloaded") { log.Info().Msg("installing kubefirst dependencies") err := k3d.DownloadTools(clusterNameFlag, config.GitProvider, cGitOwner, config.ToolsDir, config.GitProtocol) if err != nil { - return err + return fmt.Errorf("failed to download tools: %w", err) } log.Info().Msg("download dependencies `$HOME/.k1/tools` complete") @@ -557,13 +481,10 @@ func runK3d(cmd *cobra.Command, args []string) error { MetaphorProductionIngressURL: fmt.Sprintf("metaphor-production.%s", k3d.DomainName), } - //* git clone and detokenize the gitops repository - // todo improve this logic for removing `kubefirst clean` - // if !viper.GetBool("template-repo.gitops.cloned") || viper.GetBool("template-repo.gitops.removed") { - progressPrinter.IncrementTracker("preflight-checks", 1) progressPrinter.IncrementTracker("preflight-checks", 1) progressPrinter.AddTracker("cloning-and-formatting-git-repositories", "Cloning and formatting git repositories", 1) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) + removeAtlantis := false if viper.GetString("secrets.atlantis-ngrok-authtoken") == "" { removeAtlantis = true @@ -574,11 +495,11 @@ func runK3d(cmd *cobra.Command, args []string) error { config.GitProvider, clusterNameFlag, clusterTypeFlag, - config.DestinationGitopsRepoURL, // default to https for git interactions when creating remotes + config.DestinationGitopsRepoURL, config.GitopsDir, gitopsTemplateBranchFlag, gitopsTemplateURLFlag, - config.DestinationMetaphorRepoURL, // default to https for git interactions when creating remotes + config.DestinationMetaphorRepoURL, config.K1Dir, &gitopsDirectoryTokens, config.MetaphorDir, @@ -587,10 +508,9 @@ func runK3d(cmd *cobra.Command, args []string) error { removeAtlantis, ) if err != nil { - return err + return fmt.Errorf("failed to prepare git repositories: %w", err) } - // todo emit init telemetry end viper.Set("kubefirst-checks.gitops-ready-to-push", true) viper.WriteConfig() progressPrinter.IncrementTracker("cloning-and-formatting-git-repositories", 1) @@ -604,7 +524,6 @@ func runK3d(cmd *cobra.Command, args []string) error { switch config.GitProvider { case "github": - // //* create teams and repositories in github executionControl = viper.GetBool("kubefirst-checks.terraform-apply-github") if !executionControl { telemetry.SendEvent(segClient, telemetry.GitTerraformApplyStarted, "") @@ -612,25 +531,24 @@ func runK3d(cmd *cobra.Command, args []string) error { log.Info().Msg("Creating GitHub resources with Terraform") tfEntrypoint := config.GitopsDir + "/terraform/github" - tfEnvs := map[string]string{} - // tfEnvs = k3d.GetGithubTerraformEnvs(tfEnvs) - tfEnvs["GITHUB_TOKEN"] = cGitToken - tfEnvs["GITHUB_OWNER"] = cGitOwner - tfEnvs["TF_VAR_kbot_ssh_public_key"] = viper.GetString("kbot.public-key") - tfEnvs["AWS_ACCESS_KEY_ID"] = constants.MinioDefaultUsername - tfEnvs["AWS_SECRET_ACCESS_KEY"] = constants.MinioDefaultPassword - tfEnvs["TF_VAR_aws_access_key_id"] = constants.MinioDefaultUsername - tfEnvs["TF_VAR_aws_secret_access_key"] = constants.MinioDefaultPassword - // Erase public key to prevent it from being created if the git protocol argument is set to htps - switch config.GitProtocol { - case "https": + tfEnvs := map[string]string{ + "GITHUB_TOKEN": cGitToken, + "GITHUB_OWNER": cGitOwner, + "TF_VAR_kbot_ssh_public_key": viper.GetString("kbot.public-key"), + "AWS_ACCESS_KEY_ID": constants.MinioDefaultUsername, + "AWS_SECRET_ACCESS_KEY": constants.MinioDefaultPassword, + "TF_VAR_aws_access_key_id": constants.MinioDefaultUsername, + "TF_VAR_aws_secret_access_key": constants.MinioDefaultPassword, + } + if config.GitProtocol == "https" { tfEnvs["TF_VAR_kbot_ssh_public_key"] = "" } + err := terraform.InitApplyAutoApprove(config.TerraformClient, tfEntrypoint, tfEnvs) if err != nil { - msg := fmt.Sprintf("error creating github resources with terraform %s: %s", tfEntrypoint, err) - telemetry.SendEvent(segClient, telemetry.GitTerraformApplyFailed, msg) - return errors.New(msg) + msg := fmt.Errorf("error creating GitHub resources with terraform %q: %w", tfEntrypoint, err) + telemetry.SendEvent(segClient, telemetry.GitTerraformApplyFailed, msg.Error()) + return msg } log.Info().Msgf("created git repositories for github.com/%s", cGitOwner) @@ -643,7 +561,6 @@ func runK3d(cmd *cobra.Command, args []string) error { progressPrinter.IncrementTracker("applying-git-terraform", 1) } case "gitlab": - // //* create teams and repositories in gitlab executionControl = viper.GetBool("kubefirst-checks.terraform-apply-gitlab") if !executionControl { telemetry.SendEvent(segClient, telemetry.GitTerraformApplyStarted, "") @@ -651,25 +568,25 @@ func runK3d(cmd *cobra.Command, args []string) error { log.Info().Msg("Creating GitLab resources with Terraform") tfEntrypoint := config.GitopsDir + "/terraform/gitlab" - tfEnvs := map[string]string{} - tfEnvs["GITLAB_TOKEN"] = cGitToken - tfEnvs["GITLAB_OWNER"] = gitlabGroupFlag - tfEnvs["TF_VAR_owner_group_id"] = strconv.Itoa(cGitlabOwnerGroupID) - tfEnvs["TF_VAR_kbot_ssh_public_key"] = viper.GetString("kbot.public-key") - tfEnvs["AWS_ACCESS_KEY_ID"] = constants.MinioDefaultUsername - tfEnvs["AWS_SECRET_ACCESS_KEY"] = constants.MinioDefaultPassword - tfEnvs["TF_VAR_aws_access_key_id"] = constants.MinioDefaultUsername - tfEnvs["TF_VAR_aws_secret_access_key"] = constants.MinioDefaultPassword - // Erase public key to prevent it from being created if the git protocol argument is set to htps - switch config.GitProtocol { - case "https": + tfEnvs := map[string]string{ + "GITLAB_TOKEN": cGitToken, + "GITLAB_OWNER": gitlabGroupFlag, + "TF_VAR_owner_group_id": strconv.Itoa(cGitlabOwnerGroupID), + "TF_VAR_kbot_ssh_public_key": viper.GetString("kbot.public-key"), + "AWS_ACCESS_KEY_ID": constants.MinioDefaultUsername, + "AWS_SECRET_ACCESS_KEY": constants.MinioDefaultPassword, + "TF_VAR_aws_access_key_id": constants.MinioDefaultUsername, + "TF_VAR_aws_secret_access_key": constants.MinioDefaultPassword, + } + if config.GitProtocol == "https" { tfEnvs["TF_VAR_kbot_ssh_public_key"] = "" } + err := terraform.InitApplyAutoApprove(config.TerraformClient, tfEntrypoint, tfEnvs) if err != nil { - msg := fmt.Sprintf("error creating gitlab resources with terraform %s: %s", tfEntrypoint, err) - telemetry.SendEvent(segClient, telemetry.GitTerraformApplyFailed, msg) - return errors.New(msg) + msg := fmt.Errorf("error creating GitLab resources with terraform %q: %w", tfEntrypoint, err) + telemetry.SendEvent(segClient, telemetry.GitTerraformApplyFailed, msg.Error()) + return msg } log.Info().Msgf("created git projects and groups for gitlab.com/%s", gitlabGroupFlag) @@ -683,12 +600,11 @@ func runK3d(cmd *cobra.Command, args []string) error { } } - //* push detokenized gitops-template repository content to new remote progressPrinter.AddTracker("pushing-gitops-repos-upstream", "Pushing git repositories", 1) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) - log.Info().Msgf("referencing gitops repository: %s", config.DestinationGitopsRepoGitURL) - log.Info().Msgf("referencing metaphor repository: %s", config.DestinationMetaphorRepoURL) + log.Info().Msgf("referencing gitops repository: %q", config.DestinationGitopsRepoGitURL) + log.Info().Msgf("referencing metaphor repository: %q", config.DestinationMetaphorRepoURL) executionControl = viper.GetBool("kubefirst-checks.gitops-repo-pushed") if !executionControl { @@ -696,12 +612,12 @@ func runK3d(cmd *cobra.Command, args []string) error { gitopsRepo, err := git.PlainOpen(config.GitopsDir) if err != nil { - log.Info().Msgf("error opening repo at: %s", config.GitopsDir) + return fmt.Errorf("error opening repo at %q: %w", config.GitopsDir, err) } metaphorRepo, err := git.PlainOpen(config.MetaphorDir) if err != nil { - log.Info().Msgf("error opening repo at: %s", config.MetaphorDir) + return fmt.Errorf("error opening repo at %q: %w", config.MetaphorDir, err) } err = utils.EvalSSHKey(&types.EvalSSHKeyRequest{ @@ -710,11 +626,9 @@ func runK3d(cmd *cobra.Command, args []string) error { GitToken: cGitToken, }) if err != nil { - return err + return fmt.Errorf("failed to evaluate SSH key: %w", err) } - // Push to remotes and use https - // Push gitops repo to remote err = gitopsRepo.Push( &git.PushOptions{ RemoteName: config.GitProvider, @@ -722,14 +636,14 @@ func runK3d(cmd *cobra.Command, args []string) error { }, ) if err != nil { - msg := fmt.Sprintf("error pushing detokenized gitops repository to remote %s: %s", config.DestinationGitopsRepoGitURL, err) - telemetry.SendEvent(segClient, telemetry.GitopsRepoPushFailed, msg) - if !strings.Contains(msg, "already up-to-date") { - log.Panic().Msg(msg) + msg := fmt.Errorf("error pushing detokenized gitops repository to remote %q: %w", config.DestinationGitopsRepoGitURL, err) + telemetry.SendEvent(segClient, telemetry.GitopsRepoPushFailed, msg.Error()) + if !strings.Contains(msg.Error(), "already up-to-date") { + log.Printf(msg.Error()) + return msg } } - // push metaphor repo to remote err = metaphorRepo.Push( &git.PushOptions{ RemoteName: "origin", @@ -737,27 +651,23 @@ func runK3d(cmd *cobra.Command, args []string) error { }, ) if err != nil { - msg := fmt.Sprintf("error pushing detokenized metaphor repository to remote %s: %s", config.DestinationMetaphorRepoURL, err) - telemetry.SendEvent(segClient, telemetry.GitopsRepoPushFailed, msg) - if !strings.Contains(msg, "already up-to-date") { - log.Panic().Msg(msg) + msg := fmt.Errorf("error pushing detokenized metaphor repository to remote %q: %w", config.DestinationMetaphorRepoURL, err) + telemetry.SendEvent(segClient, telemetry.GitopsRepoPushFailed, msg.Error()) + if !strings.Contains(msg.Error(), "already up-to-date") { + return msg } } log.Info().Msgf("successfully pushed gitops and metaphor repositories to https://%s/%s", cGitHost, cGitOwner) - // todo delete the local gitops repo and re-clone it - // todo that way we can stop worrying about which origin we're going to push to viper.Set("kubefirst-checks.gitops-repo-pushed", true) viper.WriteConfig() telemetry.SendEvent(segClient, telemetry.GitopsRepoPushCompleted, "") - progressPrinter.IncrementTracker("pushing-gitops-repos-upstream", 1) // todo verify this tracker didnt lose one + progressPrinter.IncrementTracker("pushing-gitops-repos-upstream", 1) } else { log.Info().Msg("already pushed detokenized gitops repository content") progressPrinter.IncrementTracker("pushing-gitops-repos-upstream", 1) } - //* create k3d resources - progressPrinter.AddTracker("creating-k3d-cluster", "Creating k3d cluster", 1) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) @@ -768,11 +678,11 @@ func runK3d(cmd *cobra.Command, args []string) error { err := k3d.ClusterCreate(clusterNameFlag, config.K1Dir, config.K3dClient, config.Kubeconfig) if err != nil { - msg := fmt.Sprintf("error creating k3d resources with k3d client %s: %s", config.K3dClient, err) + msg := fmt.Errorf("error creating k3d resources with k3d client %q: %w", config.K3dClient, err) viper.Set("kubefirst-checks.create-k3d-cluster-failed", true) viper.WriteConfig() - telemetry.SendEvent(segClient, telemetry.CloudTerraformApplyFailed, msg) - return errors.New(msg) + telemetry.SendEvent(segClient, telemetry.CloudTerraformApplyFailed, msg.Error()) + return msg } log.Info().Msg("successfully created k3d cluster") @@ -787,7 +697,6 @@ func runK3d(cmd *cobra.Command, args []string) error { kcfg := k8s.CreateKubeConfig(false, config.Kubeconfig) - // kubernetes.BootstrapSecrets progressPrinter.AddTracker("bootstrapping-kubernetes-resources", "Bootstrapping Kubernetes resources", 2) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) @@ -795,7 +704,7 @@ func runK3d(cmd *cobra.Command, args []string) error { if !executionControl { err := k3d.GenerateTLSSecrets(kcfg.Clientset, *config) if err != nil { - return err + return fmt.Errorf("failed to generate TLS secrets: %w", err) } err = k3d.AddK3DSecrets( @@ -811,7 +720,7 @@ func runK3d(cmd *cobra.Command, args []string) error { ) if err != nil { log.Info().Msg("Error adding kubernetes secrets for bootstrap") - return err + return fmt.Errorf("failed to add Kubernetes secrets: %w", err) } viper.Set("kubefirst-checks.k8s-secrets-created", true) viper.WriteConfig() @@ -821,25 +730,6 @@ func runK3d(cmd *cobra.Command, args []string) error { progressPrinter.IncrementTracker("bootstrapping-kubernetes-resources", 1) } - // //* check for ssl restore - // log.Info().Msg("checking for tls secrets to restore") - // secretsFilesToRestore, err := ioutil.ReadDir(config.SSLBackupDir + "/secrets") - // if err != nil { - // log.Info().Msgf("%s", err) - // } - // if len(secretsFilesToRestore) != 0 { - // // todo would like these but requires CRD's and is not currently supported - // // add crds ( use execShellReturnErrors? ) - // // https://raw.githubusercontent.com/cert-manager/cert-manager/v1.11.0/deploy/crds/crd-clusterissuers.yaml - // // https://raw.githubusercontent.com/cert-manager/cert-manager/v1.11.0/deploy/crds/crd-certificates.yaml - // // add certificates, and clusterissuers - // log.Info().Msgf("found %d tls secrets to restore", len(secretsFilesToRestore)) - // ssl.Restore(config.SSLBackupDir, k3d.DomainName, config.Kubeconfig) - // } else { - // log.Info().Msg("no files found in secrets directory, continuing") - // } - - // Container registry authentication creation containerRegistryAuth := gitShim.ContainerRegistryAuth{ GitProvider: gitProviderFlag, GitUser: cGitUser, @@ -851,15 +741,13 @@ func runK3d(cmd *cobra.Command, args []string) error { } containerRegistryAuthToken, err := gitShim.CreateContainerRegistrySecret(&containerRegistryAuth) if err != nil { - return err + return fmt.Errorf("failed to create container registry secret: %w", err) } progressPrinter.IncrementTracker("bootstrapping-kubernetes-resources", 1) - // k3d Readiness checks progressPrinter.AddTracker("verifying-k3d-cluster-readiness", "Verifying Kubernetes cluster is ready", 3) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) - // traefik traefikDeployment, err := k8s.ReturnDeploymentObject( kcfg.Clientset, "app.kubernetes.io/name", @@ -868,17 +756,14 @@ func runK3d(cmd *cobra.Command, args []string) error { 240, ) if err != nil { - log.Error().Msgf("error finding traefik deployment: %s", err) - return err + return fmt.Errorf("error finding traefik deployment: %w", err) } _, err = k8s.WaitForDeploymentReady(kcfg.Clientset, traefikDeployment, 240) if err != nil { - log.Error().Msgf("error waiting for traefik deployment ready state: %s", err) - return err + return fmt.Errorf("error waiting for traefik deployment ready state: %w", err) } progressPrinter.IncrementTracker("verifying-k3d-cluster-readiness", 1) - // metrics-server metricsServerDeployment, err := k8s.ReturnDeploymentObject( kcfg.Clientset, "k8s-app", @@ -887,13 +772,11 @@ func runK3d(cmd *cobra.Command, args []string) error { 240, ) if err != nil { - log.Error().Msgf("error finding metrics-server deployment: %s", err) - return err + return fmt.Errorf("error finding metrics-server deployment: %w", err) } _, err = k8s.WaitForDeploymentReady(kcfg.Clientset, metricsServerDeployment, 240) if err != nil { - log.Error().Msgf("error waiting for metrics-server deployment ready state: %s", err) - return err + return fmt.Errorf("error waiting for metrics-server deployment ready state: %w", err) } progressPrinter.IncrementTracker("verifying-k3d-cluster-readiness", 1) @@ -905,26 +788,24 @@ func runK3d(cmd *cobra.Command, args []string) error { progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) argoCDInstallPath := fmt.Sprintf("github.com:konstructio/manifests/argocd/k3d?ref=%s", constants.KubefirstManifestRepoRef) - //* install argo executionControl = viper.GetBool("kubefirst-checks.argocd-install") if !executionControl { telemetry.SendEvent(segClient, telemetry.ArgoCDInstallStarted, "") - log.Info().Msgf("installing argocd") + log.Info().Msgf("installing ArgoCD") - // Build and apply manifests yamlData, err := kcfg.KustomizeBuild(argoCDInstallPath) if err != nil { - return err + return fmt.Errorf("failed to build ArgoCD manifests: %w", err) } output, err := kcfg.SplitYAMLFile(yamlData) if err != nil { - return err + return fmt.Errorf("failed to split YAML file: %w", err) } err = kcfg.ApplyObjects("", output) if err != nil { telemetry.SendEvent(segClient, telemetry.ArgoCDInstallFailed, err.Error()) - return err + return fmt.Errorf("failed to apply ArgoCD objects: %w", err) } viper.Set("kubefirst-checks.argocd-install", true) @@ -932,44 +813,37 @@ func runK3d(cmd *cobra.Command, args []string) error { telemetry.SendEvent(segClient, telemetry.ArgoCDInstallCompleted, "") progressPrinter.IncrementTracker("installing-argo-cd", 1) } else { - log.Info().Msg("argo cd already installed, continuing") + log.Info().Msg("ArgoCD already installed, continuing") progressPrinter.IncrementTracker("installing-argo-cd", 1) } - // Wait for ArgoCD to be ready _, err = k8s.VerifyArgoCDReadiness(kcfg.Clientset, true, 300) if err != nil { - log.Error().Msgf("error waiting for ArgoCD to become ready: %s", err) - return err + return fmt.Errorf("error waiting for ArgoCD to become ready: %w", err) } var argocdPassword string - //* argocd pods are ready, get and set credentials executionControl = viper.GetBool("kubefirst-checks.argocd-credentials-set") if !executionControl { - log.Info().Msg("Setting argocd username and password credentials") + log.Info().Msg("Setting ArgoCD username and password credentials") argocd.ArgocdSecretClient = kcfg.Clientset.CoreV1().Secrets("argocd") argocdPassword = k8s.GetSecretValue(argocd.ArgocdSecretClient, "argocd-initial-admin-secret", "password") if argocdPassword == "" { - log.Info().Msg("argocd password not found in secret") - return err + return errors.New("ArgoCD password not found in secret") } viper.Set("components.argocd.password", argocdPassword) viper.Set("components.argocd.username", "admin") viper.WriteConfig() - log.Info().Msg("argocd username and password credentials set successfully") - log.Info().Msg("Getting an argocd auth token") + log.Info().Msg("ArgoCD username and password credentials set successfully") + log.Info().Msg("Getting an ArgoCD auth token") - // Test https to argocd var argoCDToken string - // only the host, not the protocol - err := utils.TestEndpointTLS(strings.Replace(k3d.ArgocdURL, "https://", "", 1)) - if err != nil { + if err := utils.TestEndpointTLS(strings.Replace(k3d.ArgocdURL, "https://", "", 1)); err != nil { argoCDStopChannel := make(chan struct{}, 1) - log.Info().Msgf("argocd not available via https, using http") + log.Info().Msgf("ArgoCD not available via https, using http") defer func() { close(argoCDStopChannel) }() @@ -999,78 +873,76 @@ func runK3d(cmd *cobra.Command, args []string) error { } } - log.Info().Msg("argocd admin auth token set") + log.Info().Msg("ArgoCD admin auth token set") viper.Set("components.argocd.auth-token", argoCDToken) viper.Set("kubefirst-checks.argocd-credentials-set", true) viper.WriteConfig() progressPrinter.IncrementTracker("installing-argo-cd", 1) } else { - log.Info().Msg("argo credentials already set, continuing") + log.Info().Msg("ArgoCD credentials already set, continuing") progressPrinter.IncrementTracker("installing-argo-cd", 1) } if configs.K1Version == "development" { err := clipboard.WriteAll(argocdPassword) if err != nil { - log.Error().Err(err).Msg("") + log.Error().Err(err).Msg("failed to copy ArgoCD password to clipboard") } if os.Getenv("SKIP_ARGOCD_LAUNCH") != "true" || !ciFlag { err = utils.OpenBrowser(constants.ArgoCDLocalURLTLS) if err != nil { - log.Error().Err(err).Msg("") + log.Error().Err(err).Msg("failed to open ArgoCD URL in browser") } } } - //* argocd sync registry and start sync waves executionControl = viper.GetBool("kubefirst-checks.argocd-create-registry") if !executionControl { telemetry.SendEvent(segClient, telemetry.CreateRegistryStarted, "") argocdClient, err := argocdapi.NewForConfig(kcfg.RestConfig) if err != nil { - return err + return fmt.Errorf("failed to create ArgoCD client: %w", err) } - log.Info().Msg("applying the registry application to argocd") + log.Info().Msg("applying the registry application to ArgoCD") registryApplicationObject := argocd.GetArgoCDApplicationObject(gitopsRepoURL, fmt.Sprintf("registry/%s", clusterNameFlag)) err = k3d.RestartDeployment(context.Background(), kcfg.Clientset, "argocd", "argocd-applicationset-controller") if err != nil { - return fmt.Errorf("error in restarting argocd controller %w", err) + return fmt.Errorf("error in restarting ArgoCD controller: %w", err) } err = wait.PollImmediate(5*time.Second, 20*time.Second, func() (bool, error) { _, err := argocdClient.ArgoprojV1alpha1().Applications("argocd").Create(context.Background(), registryApplicationObject, metav1.CreateOptions{}) if err != nil { if errors.Is(err, syscall.ECONNREFUSED) { - return false, nil // retry if we can't connect to it + return false, nil } if apierrors.IsAlreadyExists(err) { - return true, nil // application already exists + return true, nil } - return false, fmt.Errorf("error creating argocd application : %w", err) + return false, fmt.Errorf("error creating ArgoCD application: %w", err) } return true, nil }) if err != nil { - return fmt.Errorf("error creating argocd application : %w", err) + return fmt.Errorf("error creating ArgoCD application: %w", err) } - log.Info().Msg("Argo CD application created successfully") + log.Info().Msg("ArgoCD application created successfully") viper.Set("kubefirst-checks.argocd-create-registry", true) viper.WriteConfig() telemetry.SendEvent(segClient, telemetry.CreateRegistryCompleted, "") progressPrinter.IncrementTracker("installing-argo-cd", 1) } else { - log.Info().Msg("argocd registry create already done, continuing") + log.Info().Msg("ArgoCD registry create already done, continuing") progressPrinter.IncrementTracker("installing-argo-cd", 1) } - // Wait for Vault StatefulSet Pods to transition to Running progressPrinter.AddTracker("configuring-vault", "Configuring Vault", 4) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) @@ -1082,19 +954,14 @@ func runK3d(cmd *cobra.Command, args []string) error { 120, ) if err != nil { - log.Error().Msgf("Error finding Vault StatefulSet: %s", err) - return err + return fmt.Errorf("error finding Vault StatefulSet: %w", err) } _, err = k8s.WaitForStatefulSetReady(kcfg.Clientset, vaultStatefulSet, 120, true) if err != nil { - log.Error().Msgf("Error waiting for Vault StatefulSet ready state: %s", err) - return err + return fmt.Errorf("error waiting for Vault StatefulSet ready state: %w", err) } progressPrinter.IncrementTracker("configuring-vault", 1) - // Init and unseal vault - // We need to wait before we try to run any of these commands or there may be - // unexpected timeouts time.Sleep(time.Second * 10) progressPrinter.IncrementTracker("configuring-vault", 1) @@ -1102,33 +969,30 @@ func runK3d(cmd *cobra.Command, args []string) error { if !executionControl { telemetry.SendEvent(segClient, telemetry.VaultInitializationStarted, "") - // Initialize and unseal Vault vaultHandlerPath := "github.com:kubefirst/manifests.git/vault-handler/replicas-1" - // Build and apply manifests yamlData, err := kcfg.KustomizeBuild(vaultHandlerPath) if err != nil { - return err + return fmt.Errorf("failed to build vault handler manifests: %w", err) } output, err := kcfg.SplitYAMLFile(yamlData) if err != nil { - return err + return fmt.Errorf("failed to split YAML file: %w", err) } err = kcfg.ApplyObjects("", output) if err != nil { - return err + return fmt.Errorf("failed to apply vault handler objects: %w", err) } - // Wait for the Job to finish job, err := k8s.ReturnJobObject(kcfg.Clientset, "vault", "vault-handler") if err != nil { - return err + return fmt.Errorf("failed to get vault job object: %w", err) } _, err = k8s.WaitForJobComplete(kcfg.Clientset, job, 240) if err != nil { - msg := fmt.Sprintf("could not run vault unseal job: %s", err) - telemetry.SendEvent(segClient, telemetry.VaultInitializationFailed, msg) - log.Fatal().Msg(msg) + msg := fmt.Errorf("could not run vault unseal job: %w", err) + telemetry.SendEvent(segClient, telemetry.VaultInitializationFailed, msg.Error()) + return msg } viper.Set("kubefirst-checks.vault-initialized", true) @@ -1144,6 +1008,7 @@ func runK3d(cmd *cobra.Command, args []string) error { defer func() { close(minioStopChannel) }() + k8s.OpenPortForwardPodWrapper( kcfg.Clientset, kcfg.RestConfig, @@ -1154,44 +1019,40 @@ func runK3d(cmd *cobra.Command, args []string) error { minioStopChannel, ) - // Initialize minio client object. minioClient, err := minio.New(constants.MinioPortForwardEndpoint, &minio.Options{ Creds: credentials.NewStaticV4(constants.MinioDefaultUsername, constants.MinioDefaultPassword, ""), Secure: false, Region: constants.MinioRegion, }) if err != nil { - log.Info().Msgf("Error creating Minio client: %s", err) + return fmt.Errorf("error creating Minio client: %w", err) } - // define upload object objectName := fmt.Sprintf("terraform/%s/terraform.tfstate", config.GitProvider) filePath := config.K1Dir + fmt.Sprintf("/gitops/%s", objectName) contentType := "xl.meta" bucketName := "kubefirst-state-store" - log.Info().Msgf("BucketName: %s", bucketName) + log.Info().Msgf("BucketName: %q", bucketName) viper.Set("kubefirst.state-store.name", bucketName) viper.Set("kubefirst.state-store.hostname", "minio-console.kubefirst.dev") viper.Set("kubefirst.state-store-creds.access-key-id", constants.MinioDefaultUsername) viper.Set("kubefirst.state-store-creds.secret-access-key-id", constants.MinioDefaultPassword) - // Upload the zip file with FPutObject info, err := minioClient.FPutObject(ctx, bucketName, objectName, filePath, minio.PutObjectOptions{ContentType: contentType}) if err != nil { - log.Info().Msgf("Error uploading to Minio bucket: %s", err) + return fmt.Errorf("error uploading to Minio bucket: %w", err) } - log.Printf("Successfully uploaded %s to bucket %s\n", objectName, info.Bucket) + log.Printf("Successfully uploaded %q to bucket %q", objectName, info.Bucket) progressPrinter.IncrementTracker("configuring-vault", 1) - //* configure vault with terraform - //* vault port-forward vaultStopChannel := make(chan struct{}, 1) defer func() { close(vaultStopChannel) }() + k8s.OpenPortForwardPodWrapper( kcfg.Clientset, kcfg.RestConfig, @@ -1202,32 +1063,23 @@ func runK3d(cmd *cobra.Command, args []string) error { vaultStopChannel, ) - // Retrieve root token from init step var vaultRootToken string secData, err := k8s.ReadSecretV2(kcfg.Clientset, "vault", "vault-unseal-secret") if err != nil { - return err + return fmt.Errorf("failed to read vault unseal secret: %w", err) } vaultRootToken = secData["root-token"] - // Parse k3d api endpoint from kubeconfig - // In this case, we need to get the IP of the in-cluster API server to provide to Vault - // to work with Kubernetes auth kubernetesInClusterAPIService, err := k8s.ReadService(config.Kubeconfig, "default", "kubernetes") if err != nil { - log.Error().Msgf("error looking up kubernetes api server service: %s", err) - return err + return fmt.Errorf("error looking up kubernetes api server service: %w", err) } - err = utils.TestEndpointTLS(strings.Replace(k3d.VaultURL, "https://", "", 1)) - if err != nil { - return fmt.Errorf( - "unable to reach vault over https - this is likely due to the mkcert certificate store missing. please install it via `%s -install`", config.MkCertClient, - ) + if err := utils.TestEndpointTLS(strings.Replace(k3d.VaultURL, "https://", "", 1)); err != nil { + return fmt.Errorf("unable to reach vault over https: %w", err) } - //* configure vault with terraform executionControl = viper.GetBool("kubefirst-checks.terraform-apply-vault") if !executionControl { telemetry.SendEvent(segClient, telemetry.VaultTerraformApplyStarted, "") @@ -1244,7 +1096,6 @@ func runK3d(cmd *cobra.Command, args []string) error { } else { usernamePasswordString = fmt.Sprintf("%s:%s", cGitUser, cGitToken) base64DockerAuth = base64.StdEncoding.EncodeToString([]byte(usernamePasswordString)) - } log.Info().Msg("configuring vault with terraform") @@ -1267,15 +1118,13 @@ func runK3d(cmd *cobra.Command, args []string) error { tfEnvs["TF_VAR_aws_access_key_id"] = constants.MinioDefaultUsername tfEnvs["TF_VAR_aws_secret_access_key"] = constants.MinioDefaultPassword tfEnvs["TF_VAR_ngrok_authtoken"] = viper.GetString("secrets.atlantis-ngrok-authtoken") - // tfEnvs["TF_LOG"] = "DEBUG" tfEntrypoint := config.GitopsDir + "/terraform/vault" err := terraform.InitApplyAutoApprove(config.TerraformClient, tfEntrypoint, tfEnvs) if err != nil { telemetry.SendEvent(segClient, telemetry.VaultTerraformApplyStarted, err.Error()) - return err + return fmt.Errorf("failed to execute vault terraform: %w", err) } - log.Info().Msg("vault terraform executed successfully") viper.Set("kubefirst-checks.terraform-apply-vault", true) viper.WriteConfig() @@ -1286,7 +1135,6 @@ func runK3d(cmd *cobra.Command, args []string) error { progressPrinter.IncrementTracker("configuring-vault", 1) } - //* create users progressPrinter.AddTracker("creating-users", "Creating users", 1) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) @@ -1310,10 +1158,9 @@ func runK3d(cmd *cobra.Command, args []string) error { err := terraform.InitApplyAutoApprove(config.TerraformClient, tfEntrypoint, tfEnvs) if err != nil { telemetry.SendEvent(segClient, telemetry.UsersTerraformApplyStarted, err.Error()) - return err + return fmt.Errorf("failed to apply users terraform: %w", err) } log.Info().Msg("executed users terraform successfully") - // progressPrinter.IncrementTracker("step-users", 1) viper.Set("kubefirst-checks.terraform-apply-users", true) viper.WriteConfig() telemetry.SendEvent(segClient, telemetry.UsersTerraformApplyCompleted, "") @@ -1323,66 +1170,50 @@ func runK3d(cmd *cobra.Command, args []string) error { progressPrinter.IncrementTracker("creating-users", 1) } - // PostRun string replacement progressPrinter.AddTracker("wrapping-up", "Wrapping up", 2) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) - err = k3d.PostRunPrepareGitopsRepository(clusterNameFlag, - config.GitopsDir, - &gitopsDirectoryTokens, - ) + err = k3d.PostRunPrepareGitopsRepository(clusterNameFlag, config.GitopsDir, &gitopsDirectoryTokens) if err != nil { - log.Info().Msgf("Error detokenize post run: %s", err) + return fmt.Errorf("error detokenizing post run: %w", err) } gitopsRepo, err := git.PlainOpen(config.GitopsDir) if err != nil { - log.Info().Msgf("error opening repo at: %s", config.GitopsDir) + return fmt.Errorf("error opening repo at %q: %w", config.GitopsDir, err) } - // check if file exists before rename _, err = os.Stat(fmt.Sprintf("%s/terraform/%s/remote-backend.md", config.GitopsDir, config.GitProvider)) if err == nil { err = os.Rename(fmt.Sprintf("%s/terraform/%s/remote-backend.md", config.GitopsDir, config.GitProvider), fmt.Sprintf("%s/terraform/%s/remote-backend.tf", config.GitopsDir, config.GitProvider)) if err != nil { - return err + return fmt.Errorf("failed to rename remote-backend.md to remote-backend.tf: %w", err) } } viper.Set("kubefirst-checks.post-detokenize", true) viper.WriteConfig() - // Final gitops repo commit and push err = gitClient.Commit(gitopsRepo, "committing initial detokenized gitops-template repo content post run") if err != nil { - return err + return fmt.Errorf("failed to commit initial detokenized gitops-template repo content: %w", err) } err = gitopsRepo.Push(&git.PushOptions{ RemoteName: config.GitProvider, Auth: httpAuth, }) if err != nil { - log.Info().Msgf("Error pushing repo: %s", err) + return fmt.Errorf("failed to push initial detokenized gitops-template repo content: %w", err) } progressPrinter.IncrementTracker("wrapping-up", 1) - // Wait for console Deployment Pods to transition to Running - argoDeployment, err := k8s.ReturnDeploymentObject( - kcfg.Clientset, - "app.kubernetes.io/instance", - "argo", - "argo", - 1200, - ) + argoDeployment, err := k8s.ReturnDeploymentObject(kcfg.Clientset, "app.kubernetes.io/instance", "argo", "argo", 1200) if err != nil { - log.Error().Msgf("Error finding argo workflows Deployment: %s", err) - return err + return fmt.Errorf("error finding Argo Workflows Deployment: %w", err) } _, err = k8s.WaitForDeploymentReady(kcfg.Clientset, argoDeployment, 120) if err != nil { - log.Error().Msgf("Error waiting for argo workflows Deployment ready state: %s", err) - return err + return fmt.Errorf("error waiting for Argo Workflows Deployment ready state: %w", err) } - // Set flags used to track status of active options utils.SetClusterStatusFlags(k3d.CloudProvider, config.GitProvider) cluster := utilities.CreateClusterRecordFromRaw(useTelemetryFlag, cGitOwner, cGitUser, cGitToken, cGitlabOwnerGroupID, gitopsTemplateURLFlag, gitopsTemplateBranchFlag, catalogApps) @@ -1393,7 +1224,7 @@ func runK3d(cmd *cobra.Command, args []string) error { viper.Set("kubefirst.setup-complete", false) viper.Set("kubefirst-checks.cluster-install-complete", false) viper.WriteConfig() - return err + return fmt.Errorf("failed to export cluster object: %w", err) } else { kubefirstDeployment, err := k8s.ReturnDeploymentObject( kcfg.Clientset, @@ -1403,29 +1234,26 @@ func runK3d(cmd *cobra.Command, args []string) error { 600, ) if err != nil { - log.Error().Msgf("Error finding kubefirst Deployment: %s", err) - return err + return fmt.Errorf("error finding kubefirst Deployment: %w", err) } _, err = k8s.WaitForDeploymentReady(kcfg.Clientset, kubefirstDeployment, 120) if err != nil { - log.Error().Msgf("Error waiting for kubefirst Deployment ready state: %s", err) - return err + return fmt.Errorf("error waiting for kubefirst Deployment ready state: %w", err) } progressPrinter.IncrementTracker("wrapping-up", 1) err = utils.OpenBrowser(constants.KubefirstConsoleLocalURLTLS) if err != nil { - log.Error().Err(err).Msg("") + log.Error().Err(err).Msg("failed to open Kubefirst console in browser") } - // Mark cluster install as complete telemetry.SendEvent(segClient, telemetry.ClusterInstallCompleted, "") viper.Set("kubefirst-checks.cluster-install-complete", true) viper.WriteConfig() log.Info().Msg("kubefirst installation complete") - log.Info().Msg("welcome to your new kubefirst platform running in K3d") - time.Sleep(time.Second * 1) // allows progress bars to finish + log.Info().Msg("welcome to your new Kubefirst platform running in K3D") + time.Sleep(1 * time.Second) reports.LocalHandoffScreenV2(viper.GetString("components.argocd.password"), clusterNameFlag, gitDestDescriptor, cGitOwner, config, ciFlag) diff --git a/cmd/k3d/destroy.go b/cmd/k3d/destroy.go index 3a3782b1..e487d777 100644 --- a/cmd/k3d/destroy.go +++ b/cmd/k3d/destroy.go @@ -14,23 +14,21 @@ import ( "time" constants "github.com/konstructio/kubefirst-api/pkg/constants" - utils "github.com/konstructio/kubefirst-api/pkg/utils" - gitlab "github.com/konstructio/kubefirst-api/pkg/gitlab" "github.com/konstructio/kubefirst-api/pkg/k3d" "github.com/konstructio/kubefirst-api/pkg/k8s" "github.com/konstructio/kubefirst-api/pkg/progressPrinter" "github.com/konstructio/kubefirst-api/pkg/terraform" + utils "github.com/konstructio/kubefirst-api/pkg/utils" "github.com/konstructio/kubefirst/internal/progress" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" ) -func destroyK3d(cmd *cobra.Command, args []string) error { +func destroyK3d(_ *cobra.Command, _ []string) error { utils.DisplayLogHints() - // Determine if there are active installs gitProvider := viper.GetString("flags.git-provider") clusterName := viper.GetString("flags.cluster-name") gitProtocol := viper.GetString("flags.git-protocol") @@ -40,11 +38,8 @@ func destroyK3d(cmd *cobra.Command, args []string) error { progress.Progress.Quit() } - // Check for existing port forwards before continuing - err := k8s.CheckForExistingPortForwards(9000) - if err != nil { - log.Error().Msgf("%s - this port is required to tear down your kubefirst environment - please close any existing port forwards before continuing", err.Error()) - return fmt.Errorf("%s (maybe the handoff screen is still open in another terminal) - this port is required to tear down your kubefirst environment - please close any existing port forwards before continuing", err.Error()) + if err := k8s.CheckForExistingPortForwards(9000); err != nil { + return fmt.Errorf("%w - this port is required to tear down your kubefirst environment - please close any existing port forwards before continuing", err) } progressPrinter.AddTracker("preflight-checks", "Running preflight checks", 1) @@ -55,7 +50,6 @@ func destroyK3d(cmd *cobra.Command, args []string) error { atlantisWebhookURL := fmt.Sprintf("%s/events", viper.GetString("ngrok.host")) - // Switch based on git provider, set params var cGitOwner, cGitToken string switch gitProvider { case "github": @@ -65,10 +59,9 @@ func destroyK3d(cmd *cobra.Command, args []string) error { cGitOwner = viper.GetString("flags.gitlab-owner") cGitToken = os.Getenv("GITLAB_TOKEN") default: - log.Panic().Msgf("invalid git provider option") + return fmt.Errorf("invalid git provider option: %q", gitProvider) } - // Instantiate K3d config config := k3d.GetConfig(clusterName, gitProvider, cGitOwner, gitProtocol) switch gitProvider { case "github": @@ -77,12 +70,8 @@ func destroyK3d(cmd *cobra.Command, args []string) error { config.GitlabToken = cGitToken } - log.Info().Msg("destroying kubefirst platform running in k3d") - kcfg := k8s.CreateKubeConfig(false, config.Kubeconfig) - // todo improve these checks, make them standard for - // both create and destroy if len(cGitToken) == 0 { return fmt.Errorf( "please set a %s_TOKEN environment variable to continue\n https://docs.kubefirst.io/kubefirst/%s/install.html#step-3-kubefirst-init", @@ -91,24 +80,15 @@ func destroyK3d(cmd *cobra.Command, args []string) error { } if viper.GetBool("kubefirst-checks.post-detokenize") { - // Temporary func to allow destroy - err = k3d.ResolveMinioLocal(fmt.Sprintf("%s/terraform", config.GitopsDir)) - if err != nil { - log.Fatal().Msgf("error preloading files for terraform destroy: %s", err) + if err := k3d.ResolveMinioLocal(fmt.Sprintf("%s/terraform", config.GitopsDir)); err != nil { + return fmt.Errorf("unable to preload files for terraform destroy: %w", err) } minioStopChannel := make(chan struct{}, 1) defer func() { close(minioStopChannel) }() - k8s.OpenPortForwardPodWrapper( - kcfg.Clientset, - kcfg.RestConfig, - "minio", - "minio", - 9000, - 9000, - minioStopChannel, - ) + + k8s.OpenPortForwardPodWrapper(kcfg.Clientset, kcfg.RestConfig, "minio", "minio", 9000, 9000, minioStopChannel) } progressPrinter.IncrementTracker("preflight-checks", 1) @@ -119,22 +99,20 @@ func destroyK3d(cmd *cobra.Command, args []string) error { log.Info().Msg("destroying github resources with terraform") tfEntrypoint := config.GitopsDir + "/terraform/github" - tfEnvs := map[string]string{} - - tfEnvs["GITHUB_TOKEN"] = cGitToken - tfEnvs["GITHUB_OWNER"] = cGitOwner - tfEnvs["TF_VAR_atlantis_repo_webhook_secret"] = viper.GetString("secrets.atlantis-webhook") - tfEnvs["TF_VAR_atlantis_repo_webhook_url"] = atlantisWebhookURL - tfEnvs["TF_VAR_kbot_ssh_public_key"] = viper.GetString("kbot.public-key") - tfEnvs["AWS_ACCESS_KEY_ID"] = constants.MinioDefaultUsername - tfEnvs["AWS_SECRET_ACCESS_KEY"] = constants.MinioDefaultPassword - tfEnvs["TF_VAR_aws_access_key_id"] = constants.MinioDefaultUsername - tfEnvs["TF_VAR_aws_secret_access_key"] = constants.MinioDefaultPassword - - err := terraform.InitDestroyAutoApprove(config.TerraformClient, tfEntrypoint, tfEnvs) - if err != nil { - log.Printf("error executing terraform destroy %s", tfEntrypoint) - return err + tfEnvs := map[string]string{ + "GITHUB_TOKEN": cGitToken, + "GITHUB_OWNER": cGitOwner, + "TF_VAR_atlantis_repo_webhook_secret": viper.GetString("secrets.atlantis-webhook"), + "TF_VAR_atlantis_repo_webhook_url": atlantisWebhookURL, + "TF_VAR_kbot_ssh_public_key": viper.GetString("kbot.public-key"), + "AWS_ACCESS_KEY_ID": constants.MinioDefaultUsername, + "AWS_SECRET_ACCESS_KEY": constants.MinioDefaultPassword, + "TF_VAR_aws_access_key_id": constants.MinioDefaultUsername, + "TF_VAR_aws_secret_access_key": constants.MinioDefaultPassword, + } + + if err := terraform.InitDestroyAutoApprove(config.TerraformClient, tfEntrypoint, tfEnvs); err != nil { + return fmt.Errorf("unable to execute terraform destroy: %w", err) } viper.Set("kubefirst-checks.terraform-apply-github", false) viper.WriteConfig() @@ -146,52 +124,48 @@ func destroyK3d(cmd *cobra.Command, args []string) error { log.Info().Msg("destroying gitlab resources with terraform") gitlabClient, err := gitlab.NewGitLabClient(cGitToken, cGitOwner) if err != nil { - return err + return fmt.Errorf("unable to create gitlab client: %w", err) } - // Before removing Terraform resources, remove any container registry repositories - // since failing to remove them beforehand will result in an apply failure projectsForDeletion := []string{"gitops", "metaphor"} for _, project := range projectsForDeletion { projectExists, err := gitlabClient.CheckProjectExists(project) if err != nil { - log.Fatal().Msgf("could not check for existence of project %s: %s", project, err) + return fmt.Errorf("unable to check existence of project %q: %w", project, err) } if projectExists { - log.Info().Msgf("checking project %s for container registries...", project) + log.Info().Msgf("checking project %q for container registries...", project) crr, err := gitlabClient.GetProjectContainerRegistryRepositories(project) if err != nil { - log.Fatal().Msgf("could not retrieve container registry repositories: %s", err) + return fmt.Errorf("unable to retrieve container registry repositories: %w", err) } if len(crr) > 0 { for _, cr := range crr { - err := gitlabClient.DeleteContainerRegistryRepository(project, cr.ID) - if err != nil { - log.Fatal().Msgf("error deleting container registry repository: %s", err) + if err := gitlabClient.DeleteContainerRegistryRepository(project, cr.ID); err != nil { + return fmt.Errorf("unable to delete container registry repository %q: %w", cr.Path, err) } } } else { - log.Info().Msgf("project %s does not have any container registries, skipping", project) + log.Info().Msgf("project %q does not have any container registries, skipping", project) } } else { - log.Info().Msgf("project %s does not exist, skipping", project) + log.Info().Msgf("project %q does not exist, skipping", project) } } tfEntrypoint := config.GitopsDir + "/terraform/gitlab" - tfEnvs := map[string]string{} - - tfEnvs["GITLAB_TOKEN"] = cGitToken - tfEnvs["GITLAB_OWNER"] = cGitOwner - tfEnvs["TF_VAR_atlantis_repo_webhook_secret"] = viper.GetString("secrets.atlantis-webhook") - tfEnvs["TF_VAR_atlantis_repo_webhook_url"] = atlantisWebhookURL - tfEnvs["TF_VAR_owner_group_id"] = strconv.Itoa(gitlabClient.ParentGroupID) + tfEnvs := map[string]string{ + "GITLAB_TOKEN": cGitToken, + "GITLAB_OWNER": cGitOwner, + "TF_VAR_atlantis_repo_webhook_secret": viper.GetString("secrets.atlantis-webhook"), + "TF_VAR_atlantis_repo_webhook_url": atlantisWebhookURL, + "TF_VAR_owner_group_id": strconv.Itoa(gitlabClient.ParentGroupID), + } - err = terraform.InitDestroyAutoApprove(config.TerraformClient, tfEntrypoint, tfEnvs) - if err != nil { - log.Printf("error executing terraform destroy %s", tfEntrypoint) - return err + if err := terraform.InitDestroyAutoApprove(config.TerraformClient, tfEntrypoint, tfEnvs); err != nil { + return fmt.Errorf("unable to execute terraform destroy: %w", err) } + viper.Set("kubefirst-checks.terraform-apply-gitlab", false) viper.WriteConfig() log.Info().Msg("gitlab resources terraform destroyed") @@ -202,9 +176,8 @@ func destroyK3d(cmd *cobra.Command, args []string) error { if viper.GetBool("kubefirst-checks.create-k3d-cluster") || viper.GetBool("kubefirst-checks.create-k3d-cluster-failed") { log.Info().Msg("destroying k3d resources with terraform") - err := k3d.DeleteK3dCluster(clusterName, config.K1Dir, config.K3dClient) - if err != nil { - return err + if err := k3d.DeleteK3dCluster(clusterName, config.K1Dir, config.K3dClient); err != nil { + return fmt.Errorf("unable to delete k3d cluster %q: %w", clusterName, err) } viper.Set("kubefirst-checks.create-k3d-cluster", false) @@ -213,26 +186,22 @@ func destroyK3d(cmd *cobra.Command, args []string) error { progressPrinter.IncrementTracker("platform-destroy", 1) } - // remove ssh key provided one was created if viper.GetString("kbot.gitlab-user-based-ssh-key-title") != "" { gitlabClient, err := gitlab.NewGitLabClient(cGitToken, cGitOwner) if err != nil { - return err + return fmt.Errorf("unable to create gitlab client for deleting ssh key: %w", err) } log.Info().Msg("attempting to delete managed ssh key...") - err = gitlabClient.DeleteUserSSHKey(viper.GetString("kbot.gitlab-user-based-ssh-key-title")) - if err != nil { + if err := gitlabClient.DeleteUserSSHKey(viper.GetString("kbot.gitlab-user-based-ssh-key-title")); err != nil { log.Warn().Msg(err.Error()) } } - //* remove local content and kubefirst config file for re-execution if !viper.GetBool(fmt.Sprintf("kubefirst-checks.terraform-apply-%s", gitProvider)) && !viper.GetBool("kubefirst-checks.create-k3d-cluster") { log.Info().Msg("removing previous platform content") - err := utils.ResetK1Dir(config.K1Dir) - if err != nil { - return err + if err := utils.ResetK1Dir(config.K1Dir); err != nil { + return fmt.Errorf("unable to remove previous platform content: %w", err) } log.Info().Msg("previous platform content removed") @@ -248,13 +217,12 @@ func destroyK3d(cmd *cobra.Command, args []string) error { } if _, err := os.Stat(config.K1Dir + "/kubeconfig"); !os.IsNotExist(err) { - err = os.Remove(config.K1Dir + "/kubeconfig") - if err != nil { - return fmt.Errorf("unable to delete %q folder, error: %w", config.K1Dir+"/kubeconfig", err) + if err := os.Remove(config.K1Dir + "/kubeconfig"); err != nil { + return fmt.Errorf("unable to delete %q: %w", config.K1Dir+"/kubeconfig", err) } } - time.Sleep(time.Millisecond * 200) // allows progress bars to finish - fmt.Printf("Your kubefirst platform running in %s has been destroyed.", k3d.CloudProvider) + time.Sleep(200 * time.Millisecond) + fmt.Printf("Your kubefirst platform running in %q has been destroyed.", k3d.CloudProvider) progress.Progress.Quit() return nil diff --git a/cmd/k3d/mkcert.go b/cmd/k3d/mkcert.go index 4c7851a5..8305fc54 100644 --- a/cmd/k3d/mkcert.go +++ b/cmd/k3d/mkcert.go @@ -19,17 +19,17 @@ import ( ) // mkCert creates a single certificate for a host for k3d -func mkCert(cmd *cobra.Command, args []string) error { +func mkCert(cmd *cobra.Command, _ []string) error { utils.DisplayLogHints() appNameFlag, err := cmd.Flags().GetString("application") if err != nil { - return err + return fmt.Errorf("failed to get application flag: %w", err) } appNamespaceFlag, err := cmd.Flags().GetString("namespace") if err != nil { - return err + return fmt.Errorf("failed to get namespace flag: %w", err) } flags := utils.GetClusterStatusFlags() diff --git a/cmd/k3d/root-credentials.go b/cmd/k3d/root-credentials.go index ca629a22..6c3f9b2e 100644 --- a/cmd/k3d/root-credentials.go +++ b/cmd/k3d/root-credentials.go @@ -7,6 +7,7 @@ See the LICENSE file for more details. package k3d import ( + "errors" "fmt" "github.com/konstructio/kubefirst-api/pkg/credentials" @@ -17,7 +18,7 @@ import ( "github.com/spf13/viper" ) -func getK3dRootCredentials(cmd *cobra.Command, args []string) error { +func getK3dRootCredentials(cmd *cobra.Command, _ []string) error { domainName := k3d.DomainName clusterName := viper.GetString("flags.cluster-name") gitProvider := viper.GetString("flags.git-provider") @@ -27,15 +28,15 @@ func getK3dRootCredentials(cmd *cobra.Command, args []string) error { // Parse flags a, err := cmd.Flags().GetBool("argocd") if err != nil { - return err + return fmt.Errorf("failed to get ArgoCD flag: %w", err) } k, err := cmd.Flags().GetBool("kbot") if err != nil { - return err + return fmt.Errorf("failed to get kbot flag: %w", err) } v, err := cmd.Flags().GetBool("vault") if err != nil { - return err + return fmt.Errorf("failed to get vault flag: %w", err) } opts := credentials.CredentialOptions{ CopyArgoCDPasswordToClipboard: a, @@ -46,25 +47,23 @@ func getK3dRootCredentials(cmd *cobra.Command, args []string) error { // Determine if there are eligible installs _, err = credentials.EvalAuth(k3d.CloudProvider, gitProvider) if err != nil { - return err + return fmt.Errorf("failed to evaluate auth: %w", err) } // Determine if the Kubernetes cluster is available if !viper.GetBool("kubefirst-checks.create-k3d-cluster") { - return fmt.Errorf("it looks like a kubernetes cluster has not been created yet - try again") + return errors.New("it looks like a Kubernetes cluster has not been created yet - try again") } // Instantiate kubernetes client config := k3d.GetConfig(clusterName, gitProvider, gitOwner, gitProtocol) - kcfg := k8s.CreateKubeConfig(false, config.Kubeconfig) err = credentials.ParseAuthData(kcfg.Clientset, k3d.CloudProvider, gitProvider, domainName, &opts) if err != nil { - return err + return fmt.Errorf("failed to parse auth data: %w", err) } progress.Progress.Quit() - return nil } diff --git a/cmd/k3d/vault.go b/cmd/k3d/vault.go index 8327a991..485e49bb 100644 --- a/cmd/k3d/vault.go +++ b/cmd/k3d/vault.go @@ -13,7 +13,7 @@ import ( "strings" "time" - vaultapi "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/api" "github.com/konstructio/kubefirst-api/pkg/k3d" "github.com/konstructio/kubefirst-api/pkg/k8s" utils "github.com/konstructio/kubefirst-api/pkg/utils" @@ -26,18 +26,17 @@ import ( const ( // Name for the Secret that gets created that contains root auth data - vaultSecretName string = "vault-unseal-secret" + vaultSecretName = "vault-unseal-secret" // Namespace that Vault runs in - vaultNamespace string = "vault" + vaultNamespace = "vault" // number of secret threshold Vault unseal secretThreshold = 3 ) -// unsealVault will attempt to unseal vaule again if it is currently unsealed func unsealVault(cmd *cobra.Command, args []string) error { flags := utils.GetClusterStatusFlags() if !flags.SetupComplete { - return fmt.Errorf("there doesn't appear to be an active k3d cluster") + return fmt.Errorf("failed to unseal vault: there doesn't appear to be an active k3d cluster") } config := k3d.GetConfig( viper.GetString("flags.cluster-name"), @@ -47,70 +46,63 @@ func unsealVault(cmd *cobra.Command, args []string) error { ) kcfg := k8s.CreateKubeConfig(false, config.Kubeconfig) - // Vault api client - vaultClient, err := vaultapi.NewClient(&vaultapi.Config{ + vaultClient, err := api.NewClient(&api.Config{ Address: "https://vault.kubefirst.dev", }) if err != nil { - return err + return fmt.Errorf("failed to create vault client: %w", err) } - vaultClient.CloneConfig().ConfigureTLS(&vaultapi.TLSConfig{ + vaultClient.CloneConfig().ConfigureTLS(&api.TLSConfig{ Insecure: true, }) - // Determine vault health health, err := vaultClient.Sys().Health() if err != nil { - return err + return fmt.Errorf("failed to check vault health: %w", err) } - switch health.Sealed { - case true: + if health.Sealed { node := "vault-0" existingInitResponse, err := parseExistingVaultInitSecret(kcfg.Clientset) if err != nil { - return err + return fmt.Errorf("failed to parse existing vault init secret: %w", err) } sealStatusTracking := 0 for i, shard := range existingInitResponse.Keys { if i < secretThreshold { - log.Info().Msgf("passing unseal shard %v to %s", i+1, node) + log.Info().Msgf("passing unseal shard %d to %q", i+1, node) deadline := time.Now().Add(60 * time.Second) ctx, cancel := context.WithDeadline(context.Background(), deadline) defer cancel() - // Try 5 times to pass unseal shard - for i := 0; i < 5; i++ { + for j := 0; j < 5; j++ { _, err := vaultClient.Sys().UnsealWithContext(ctx, shard) if err != nil { if errors.Is(err, context.DeadlineExceeded) { continue } - } - if i == 5 { - return fmt.Errorf("error passing unseal shard %v to %s: %w", i+1, node, err) + return fmt.Errorf("error passing unseal shard %d to %q: %w", i+1, node, err) } } - // Wait for key acceptance - for i := 0; i < 10; i++ { + for j := 0; j < 10; j++ { sealStatus, err := vaultClient.Sys().SealStatus() if err != nil { - return fmt.Errorf("error retrieving health of %s: %s", node, err) + return fmt.Errorf("error retrieving health of %q: %w", node, err) } if sealStatus.Progress > sealStatusTracking || !sealStatus.Sealed { - log.Info().Msgf("shard accepted") + log.Info().Msg("shard accepted") sealStatusTracking++ break } - log.Info().Msgf("waiting for node %s to accept unseal shard", node) - time.Sleep(time.Second * 6) + log.Info().Msgf("waiting for node %q to accept unseal shard", node) + time.Sleep(6 * time.Second) } } } - fmt.Printf("vault unsealed\n") - case false: - return fmt.Errorf("vault is already unsealed") + log.Printf("vault unsealed") + } else { + return fmt.Errorf("failed to unseal vault: vault is already unsealed") } progress.Progress.Quit() @@ -118,16 +110,12 @@ func unsealVault(cmd *cobra.Command, args []string) error { return nil } -// parseExistingVaultInitSecret returns the value of a vault initialization secret if it exists -func parseExistingVaultInitSecret(clientset *kubernetes.Clientset) (*vaultapi.InitResponse, error) { - // If vault has already been initialized, the response is formatted to contain the value - // of the initialization secret +func parseExistingVaultInitSecret(clientset *kubernetes.Clientset) (*api.InitResponse, error) { secret, err := k8s.ReadSecretV2(clientset, vaultNamespace, vaultSecretName) if err != nil { - return &vaultapi.InitResponse{}, err + return nil, fmt.Errorf("failed to read secret: %w", err) } - // Add root-unseal-key entries to slice var rkSlice []string for key, value := range secret { if strings.Contains(key, "root-unseal-key-") { @@ -135,7 +123,7 @@ func parseExistingVaultInitSecret(clientset *kubernetes.Clientset) (*vaultapi.In } } - existingInitResponse := &vaultapi.InitResponse{ + existingInitResponse := &api.InitResponse{ Keys: rkSlice, RootToken: secret["root-token"], } diff --git a/cmd/k3s/command.go b/cmd/k3s/command.go index e89e1211..7fda362f 100644 --- a/cmd/k3s/command.go +++ b/cmd/k3s/command.go @@ -92,15 +92,15 @@ func Create() *cobra.Command { createCmd.Flags().StringSliceVar(&k3sServersPrivateIpsFlag, "servers-private-ips", []string{}, "the list of k3s (servers) private ip x.x.x.x,y.y.y.y comma separated (required)") createCmd.MarkFlagRequired("servers-private-ips") createCmd.Flags().StringSliceVar(&k3sServersPublicIpsFlag, "servers-public-ips", []string{}, "the list of k3s (servers) public ip x.x.x.x,y.y.y.y comma separated (required)") - createCmd.Flags().StringSliceVar(&K3sServersArgsFlags, "servers-args", []string{"--disable traefik", "--write-kubeconfig-mode 644"}, "list of k3s extras args to add to the k3s server installation,comma separated in between quote, if --servers-publis-ips --tls-san is added to default --servers-args") + createCmd.Flags().StringSliceVar(&K3sServersArgsFlags, "servers-args", []string{"--disable traefik", "--write-kubeconfig-mode 644"}, "list of k3s extras args to add to the k3s server installation,comma separated in between quote, if --servers-public-ips --tls-san is added to default --servers-args") createCmd.Flags().StringVar(&k3sSshUserflag, "ssh-user", "root", "the user used to log into servers with ssh connection") createCmd.Flags().StringVar(&k3sSshPrivateKeyflag, "ssh-privatekey", "", "the private key used to log into servers with ssh connection") createCmd.MarkFlagRequired("ssh-privatekey") - createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "cloudflare", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders)) + createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "cloudflare", fmt.Sprintf("the dns provider - one of: %q", supportedDNSProviders)) createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)") createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the cloudProvider DNS Name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") - createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) - createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %q", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %q", supportedGitProtocolOverride)) createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using github") createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "the branch to clone for the gitops-template repository") @@ -133,7 +133,7 @@ func RootCredentials() *cobra.Command { RunE: common.GetRootCredentials, } - authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)") + authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the ArgoCD password to the clipboard (optional)") authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)") authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)") diff --git a/cmd/k3s/create.go b/cmd/k3s/create.go index a76dd5be..c1e87ae4 100644 --- a/cmd/k3s/create.go +++ b/cmd/k3s/create.go @@ -7,6 +7,7 @@ See the LICENSE file for more details. package k3s import ( + "errors" "fmt" "os" "strings" @@ -31,27 +32,30 @@ func createK3s(cmd *cobra.Command, args []string) error { cliFlags, err := utilities.GetFlags(cmd, "k3s") if err != nil { progress.Error(err.Error()) - log.Fatal().Msgf("error collecting flags: %s", err) - return nil + return fmt.Errorf("error collecting flags: %w", err) } progress.DisplayLogHints(20) isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) + if err != nil { + return fmt.Errorf("validation of catalog apps failed: %w", err) + } + if !isValid { - return err + return errors.New("catalog validation failed") } err = ValidateProvidedFlags(cliFlags.GitProvider) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("provided flags validation failed: %w", err) } // If cluster setup is complete, return clusterSetupComplete := viper.GetBool("kubefirst-checks.cluster-install-complete") if clusterSetupComplete { - err = fmt.Errorf("this cluster install process has already completed successfully") + err = errors.New("this cluster install process has already completed successfully") progress.Error(err.Error()) return nil } @@ -61,7 +65,7 @@ func createK3s(cmd *cobra.Command, args []string) error { gitAuth, err := gitShim.ValidateGitCredentials(cliFlags.GitProvider, cliFlags.GithubOrg, cliFlags.GitlabGroup) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("git credentials validation failed: %w", err) } executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider)) @@ -79,11 +83,14 @@ func createK3s(cmd *cobra.Command, args []string) error { err = gitShim.InitializeGitProvider(&initGitParameters) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("initialization of git provider failed: %w", err) } } viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider), true) - viper.WriteConfig() + + if err := viper.WriteConfig(); err != nil { + return fmt.Errorf("writing config failed: %w", err) + } k3dClusterCreationComplete := viper.GetBool("launch.deployed") isK1Debug := strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" @@ -95,6 +102,7 @@ func createK3s(cmd *cobra.Command, args []string) error { err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") + return fmt.Errorf("app availability check failed: %w", err) } provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) @@ -110,16 +118,14 @@ func ValidateProvidedFlags(gitProvider string) error { key, err := internalssh.GetHostKey("github.com") if err != nil { return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "github.com", key.Type()) } + log.Info().Msgf("%q %s", "github.com", key.Type()) case "gitlab": key, err := internalssh.GetHostKey("gitlab.com") if err != nil { return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) } + log.Info().Msgf("%q %s", "gitlab.com", key.Type()) } progress.CompleteStep("Validate provided flags") diff --git a/cmd/launch.go b/cmd/launch.go index 5431932a..89d963b8 100644 --- a/cmd/launch.go +++ b/cmd/launch.go @@ -19,8 +19,8 @@ var additionalHelmFlags []string func LaunchCommand() *cobra.Command { launchCommand := &cobra.Command{ Use: "launch", - Short: "create a local k3d cluster and launch the kubefirst console and api in it", - Long: "create a local k3d cluster and launch the kubefirst console and api in it", + Short: "create a local k3d cluster and launch the Kubefirst console and API in it", + Long: "create a local k3d cluster and launch the Kubefirst console and API in it", } // wire up new commands @@ -35,8 +35,7 @@ func launchUp() *cobra.Command { Use: "up", Short: "launch new console and api instance", TraverseChildren: true, - // PreRun: common.CheckDocker, // TODO: check runtimes when we can support more runtimes - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { launch.Up(additionalHelmFlags, false, true) }, } @@ -52,7 +51,7 @@ func launchDown() *cobra.Command { Use: "down", Short: "remove console and api instance", TraverseChildren: true, - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { launch.Down(false) }, } @@ -64,7 +63,7 @@ func launchDown() *cobra.Command { func launchCluster() *cobra.Command { launchClusterCmd := &cobra.Command{ Use: "cluster", - Short: "interact with clusters created by the kubefirst console", + Short: "interact with clusters created by the Kubefirst console", TraverseChildren: true, } @@ -77,10 +76,9 @@ func launchCluster() *cobra.Command { func launchListClusters() *cobra.Command { launchListClustersCmd := &cobra.Command{ Use: "list", - Short: "list clusters created by the kubefirst console", + Short: "list clusters created by the Kubefirst console", TraverseChildren: true, - // PreRun: common.CheckDocker, - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { launch.ListClusters() }, } @@ -92,9 +90,8 @@ func launchListClusters() *cobra.Command { func launchDeleteCluster() *cobra.Command { launchDeleteClusterCmd := &cobra.Command{ Use: "delete", - Short: "delete a cluster created by the kubefirst console", + Short: "delete a cluster created by the Kubefirst console", TraverseChildren: true, - // PreRun: common.CheckDocker, Args: func(cmd *cobra.Command, args []string) error { if err := cobra.ExactArgs(1)(cmd, args); err != nil { return fmt.Errorf("you must provide a cluster name as the only argument to this command") diff --git a/cmd/letsencrypt.go b/cmd/letsencrypt.go index 062dc4d5..ea21f05e 100644 --- a/cmd/letsencrypt.go +++ b/cmd/letsencrypt.go @@ -7,7 +7,7 @@ See the LICENSE file for more details. package cmd import ( - "fmt" + "log" "github.com/konstructio/kubefirst-api/pkg/certificates" "github.com/konstructio/kubefirst/internal/progress" @@ -20,8 +20,8 @@ var domainNameFlag string func LetsEncryptCommand() *cobra.Command { letsEncryptCommand := &cobra.Command{ Use: "letsencrypt", - Short: "interact with letsencrypt certificates for a domain", - Long: "interact with letsencrypt certificates for a domain", + Short: "interact with LetsEncrypt certificates for a domain", + Long: "interact with LetsEncrypt certificates for a domain", } // wire up new commands @@ -33,12 +33,11 @@ func LetsEncryptCommand() *cobra.Command { func status() *cobra.Command { statusCmd := &cobra.Command{ Use: "status", - Short: "check the usage statistics for a letsencrypt certificate", + Short: "check the usage statistics for a LetsEncrypt certificate", TraverseChildren: true, Run: func(cmd *cobra.Command, args []string) { - err := certificates.CheckCertificateUsage(domainNameFlag) - if err != nil { - fmt.Println(err) + if err := certificates.CheckCertificateUsage(domainNameFlag); err != nil { + log.Printf("failed to check certificate usage for domain %q: %w", domainNameFlag, err) } progress.Progress.Quit() }, diff --git a/cmd/logs.go b/cmd/logs.go index d52f5ad2..24355df0 100755 --- a/cmd/logs.go +++ b/cmd/logs.go @@ -21,7 +21,7 @@ var logsCmd = &cobra.Command{ Use: "logs", Short: "kubefirst real time logs", Long: `kubefirst real time logs`, - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, _ []string) error { provisionLogs.InitializeProvisionLogsTerminal() go func() { @@ -29,6 +29,7 @@ var logsCmd = &cobra.Command{ if err != nil { fmt.Printf("Error tailing log file: %v\n", err) progress.Progress.Quit() + return } for line := range t.Lines { @@ -36,7 +37,9 @@ var logsCmd = &cobra.Command{ } }() - provisionLogs.ProvisionLogs.Run() + if _, err := provisionLogs.ProvisionLogs.Run(); err != nil { + return fmt.Errorf("failed to run provision logs: %w", err) + } return nil }, diff --git a/cmd/reset.go b/cmd/reset.go index 7983aece..e26789cf 100755 --- a/cmd/reset.go +++ b/cmd/reset.go @@ -35,34 +35,33 @@ var resetCmd = &cobra.Command{ if v == "" { log.Info().Msg("checks map is empty, continuing") } else { - return fmt.Errorf("unable to determine contents of kubefirst-checks") + return fmt.Errorf("unable to determine contents of kubefirst-checks: unexpected type %T", v) } case map[string]interface{}: checks, err := parseConfigEntryKubefirstChecks(v) if err != nil { - log.Error().Msgf("error: %s - resetting directory without checks", err) + log.Error().Msgf("error occurred during check parsing: %s - resetting directory without checks", err) } // If destroy hasn't been run yet, reset should fail to avoid orphaned resources switch { case checks[fmt.Sprintf("terraform-apply-%s", gitProvider)]: return fmt.Errorf( - "it looks like there's an active %s resource deployment - please run %s destroy before continuing", - gitProvider, - cloudProvider, + "it looks like there's an active %s resource deployment - please run `%s destroy` before continuing", + gitProvider, cloudProvider, ) case checks[fmt.Sprintf("terraform-apply-%s", cloudProvider)]: return fmt.Errorf( "it looks like there's an active %s installation - please run `%s destroy` before continuing", - cloudProvider, - cloudProvider, + cloudProvider, cloudProvider, ) } default: return fmt.Errorf("unable to determine contents of kubefirst-checks: unexpected type %T", v) - } - runReset() + if err := runReset(); err != nil { + return fmt.Errorf("error during reset operation: %w", err) + } return nil }, } @@ -100,18 +99,17 @@ func runReset() error { homePath, err := os.UserHomeDir() if err != nil { - return err + return fmt.Errorf("unable to get user home directory: %w", err) } k1Dir := fmt.Sprintf("%s/.k1", homePath) - err = utils.ResetK1Dir(k1Dir) - if err != nil { - return err + if err := utils.ResetK1Dir(k1Dir); err != nil { + return fmt.Errorf("error resetting k1 directory: %w", err) } log.Info().Msg("previous platform content removed") progressPrinter.IncrementTracker("removing-platform-content", 1) - log.Info().Msg("resetting `$HOME/.kubefirst` config") + log.Info().Msg("resetting $HOME/.kubefirst config") viper.Set("argocd", "") viper.Set("github", "") viper.Set("gitlab", "") @@ -120,12 +118,13 @@ func runReset() error { viper.Set("kubefirst-checks", "") viper.Set("kubefirst", "") viper.Set("secrets", "") - viper.WriteConfig() + if err := viper.WriteConfig(); err != nil { + return fmt.Errorf("error writing viper config: %w", err) + } if _, err := os.Stat(k1Dir + "/kubeconfig"); !os.IsNotExist(err) { - err = os.Remove(k1Dir + "/kubeconfig") - if err != nil { - return fmt.Errorf("unable to delete %q folder, error: %s", k1Dir+"/kubeconfig", err) + if err := os.Remove(k1Dir + "/kubeconfig"); err != nil { + return fmt.Errorf("unable to delete %q folder, error: %w", k1Dir+"/kubeconfig", err) } } diff --git a/cmd/root.go b/cmd/root.go index 2b2e7648..36411b4e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -10,14 +10,13 @@ import ( "fmt" "github.com/konstructio/kubefirst-api/pkg/configs" + "github.com/konstructio/kubefirst-api/pkg/progressPrinter" "github.com/konstructio/kubefirst/cmd/aws" "github.com/konstructio/kubefirst/cmd/civo" "github.com/konstructio/kubefirst/cmd/digitalocean" "github.com/konstructio/kubefirst/cmd/k3d" "github.com/konstructio/kubefirst/internal/common" "github.com/konstructio/kubefirst/internal/progress" - - "github.com/konstructio/kubefirst-api/pkg/progressPrinter" "github.com/spf13/cobra" ) @@ -26,13 +25,13 @@ var rootCmd = &cobra.Command{ Use: "kubefirst", Short: "kubefirst management cluster installer base command", Long: `kubefirst management cluster installer provisions an - open source application delivery platform in under an hour. + open source application delivery platform in under an hour. checkout the docs at docs.kubefirst.io.`, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { // wire viper config for flags for all commands return configs.InitializeViperConfig(cmd) }, - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { fmt.Println("To learn more about kubefirst, run:") fmt.Println(" kubefirst help") progress.Progress.Quit() @@ -47,9 +46,10 @@ func Execute() { // Before removing next line, please read ticket above. common.CheckForVersionUpdate() progressPrinter.GetInstance() - err := rootCmd.Execute() - if err != nil { - fmt.Printf("\nIf a detailed error message was available, please make the necessary corrections before retrying.\nYou can re-run the last command to try the operation again.\n\n") + if err := rootCmd.Execute(); err != nil { + fmt.Println("Error occurred during command execution:", err) + fmt.Println("If a detailed error message was available, please make the necessary corrections before retrying.") + fmt.Println("You can re-run the last command to try the operation again.") progress.Progress.Quit() } } diff --git a/cmd/terraform.go b/cmd/terraform.go index 2225d48c..54f75cd5 100644 --- a/cmd/terraform.go +++ b/cmd/terraform.go @@ -48,6 +48,7 @@ func terraformSetEnv() *cobra.Command { err := v.IterSecrets(vaultURLFlag, vaultTokenFlag, outputFileFlag) if err != nil { progress.Error(fmt.Sprintf("error during vault read: %s", err)) + return } message := ` diff --git a/cmd/version.go b/cmd/version.go index e126b8c9..3d57f95d 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -22,10 +22,10 @@ var versionCmd = &cobra.Command{ Use: "version", Short: "print the version number for kubefirst-cli", Long: `All software has versions. This is kubefirst's`, - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { versionMsg := ` ## -### kubefirst-cli golang utility version:` + fmt.Sprintf("`%s`", configs.K1Version) +### kubefirst-cli golang utility version:` + fmt.Sprintf("%s", configs.K1Version) progress.Success(versionMsg) }, diff --git a/cmd/vultr/command.go b/cmd/vultr/command.go index 0dac234b..1958a4f9 100644 --- a/cmd/vultr/command.go +++ b/cmd/vultr/command.go @@ -52,10 +52,10 @@ var ( func NewCommand() *cobra.Command { vultrCmd := &cobra.Command{ Use: "vultr", - Short: "kubefirst Vultr installation", + Short: "Kubefirst Vultr installation", Long: "kubefirst vultr", Run: func(cmd *cobra.Command, args []string) { - fmt.Println("To learn more about vultr in kubefirst, run:") + fmt.Println("To learn more about Vultr in Kubefirst, run:") fmt.Println(" kubefirst beta vultr --help") if progress.Progress != nil { @@ -76,7 +76,7 @@ func NewCommand() *cobra.Command { func Create() *cobra.Command { createCmd := &cobra.Command{ Use: "create", - Short: "create the kubefirst platform running on Vultr kubernetes", + Short: "Create the Kubefirst platform running on Vultr Kubernetes", TraverseChildren: true, RunE: createVultr, // PreRun: common.CheckDocker, @@ -85,27 +85,27 @@ func Create() *cobra.Command { vultrDefaults := constants.GetCloudDefaults().Vultr // todo review defaults and update descriptions - createCmd.Flags().StringVar(&alertsEmailFlag, "alerts-email", "", "email address for let's encrypt certificate notifications (required)") + createCmd.Flags().StringVar(&alertsEmailFlag, "alerts-email", "", "Email address for Let's Encrypt certificate notifications (required)") createCmd.MarkFlagRequired("alerts-email") - createCmd.Flags().BoolVar(&ciFlag, "ci", false, "if running kubefirst in ci, set this flag to disable interactive features") - createCmd.Flags().StringVar(&cloudRegionFlag, "cloud-region", "ewr", "the Vultr region to provision infrastructure in") - createCmd.Flags().StringVar(&clusterNameFlag, "cluster-name", "kubefirst", "the name of the cluster to create") - createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "the type of cluster to create (i.e. mgmt|workload)") - createCmd.Flags().StringVar(&nodeCountFlag, "node-count", vultrDefaults.NodeCount, "the node count for the cluster") - createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", vultrDefaults.InstanceSize, "the instance size of the cluster to create") - createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "vultr", fmt.Sprintf("the dns provider - one of: %s", supportedDNSProviders)) - createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)") - createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "the Vultr DNS Name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") + createCmd.Flags().BoolVar(&ciFlag, "ci", false, "If running Kubefirst in CI, set this flag to disable interactive features") + createCmd.Flags().StringVar(&cloudRegionFlag, "cloud-region", "ewr", "The Vultr region to provision infrastructure in") + createCmd.Flags().StringVar(&clusterNameFlag, "cluster-name", "kubefirst", "The name of the cluster to create") + createCmd.Flags().StringVar(&clusterTypeFlag, "cluster-type", "mgmt", "The type of cluster to create (i.e. mgmt|workload)") + createCmd.Flags().StringVar(&nodeCountFlag, "node-count", vultrDefaults.NodeCount, "The node count for the cluster") + createCmd.Flags().StringVar(&nodeTypeFlag, "node-type", vultrDefaults.InstanceSize, "The instance size of the cluster to create") + createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "vultr", fmt.Sprintf("The DNS provider - one of: %s", supportedDNSProviders)) + createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "The subdomain to use for DNS records (Cloudflare)") + createCmd.Flags().StringVar(&domainNameFlag, "domain-name", "", "The Vultr DNS name to use for DNS records (i.e. your-domain.com|subdomain.your-domain.com) (required)") createCmd.MarkFlagRequired("domain-name") - createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("the git provider - one of: %s", supportedGitProviders)) - createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("the git protocol - one of: %s", supportedGitProtocolOverride)) - createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "the GitHub organization for the new gitops and metaphor repositories - required if using github") - createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "the GitLab group for the new gitops and metaphor projects - required if using gitlab") - createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "the branch to clone for the gitops-template repository") - createCmd.Flags().StringVar(&gitopsTemplateURLFlag, "gitops-template-url", "https://github.com/konstructio/gitops-template.git", "the fully qualified url to the gitops-template repository to clone") - createCmd.Flags().StringVar(&installCatalogApps, "install-catalog-apps", "", "comma separated values to install after provision") - createCmd.Flags().BoolVar(&useTelemetryFlag, "use-telemetry", true, "whether to emit telemetry") - createCmd.Flags().BoolVar(&installKubefirstProFlag, "install-kubefirst-pro", true, "whether or not to install kubefirst pro") + createCmd.Flags().StringVar(&gitProviderFlag, "git-provider", "github", fmt.Sprintf("The Git provider - one of: %s", supportedGitProviders)) + createCmd.Flags().StringVar(&gitProtocolFlag, "git-protocol", "ssh", fmt.Sprintf("The Git protocol - one of: %s", supportedGitProtocolOverride)) + createCmd.Flags().StringVar(&githubOrgFlag, "github-org", "", "The GitHub organization for the new GitOps and metaphor repositories - required if using GitHub") + createCmd.Flags().StringVar(&gitlabGroupFlag, "gitlab-group", "", "The GitLab group for the new GitOps and metaphor projects - required if using GitLab") + createCmd.Flags().StringVar(&gitopsTemplateBranchFlag, "gitops-template-branch", "", "The branch to clone for the GitOps template repository") + createCmd.Flags().StringVar(&gitopsTemplateURLFlag, "gitops-template-url", "https://github.com/konstructio/gitops-template.git", "The fully qualified URL to the GitOps template repository to clone") + createCmd.Flags().StringVar(&installCatalogApps, "install-catalog-apps", "", "Comma separated values to install after provision") + createCmd.Flags().BoolVar(&useTelemetryFlag, "use-telemetry", true, "Whether to emit telemetry") + createCmd.Flags().BoolVar(&installKubefirstProFlag, "install-kubefirst-pro", true, "Whether or not to install Kubefirst Pro") return createCmd } @@ -113,8 +113,8 @@ func Create() *cobra.Command { func Destroy() *cobra.Command { destroyCmd := &cobra.Command{ Use: "destroy", - Short: "destroy the kubefirst platform", - Long: "destroy the kubefirst platform running in Vultr and remove all resources", + Short: "Destroy the Kubefirst platform", + Long: "Destroy the Kubefirst platform running in Vultr and remove all resources", RunE: common.Destroy, // PreRun: common.CheckDocker, } @@ -125,14 +125,14 @@ func Destroy() *cobra.Command { func RootCredentials() *cobra.Command { authCmd := &cobra.Command{ Use: "root-credentials", - Short: "retrieve root authentication information for platform components", - Long: "retrieve root authentication information for platform components", + Short: "Retrieve root authentication information for platform components", + Long: "Retrieve root authentication information for platform components", RunE: common.GetRootCredentials, } - authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "copy the argocd password to the clipboard (optional)") - authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "copy the kbot password to the clipboard (optional)") - authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "copy the vault password to the clipboard (optional)") + authCmd.Flags().BoolVar(©ArgoCDPasswordToClipboardFlag, "argocd", false, "Copy the ArgoCD password to the clipboard (optional)") + authCmd.Flags().BoolVar(©KbotPasswordToClipboardFlag, "kbot", false, "Copy the kbot password to the clipboard (optional)") + authCmd.Flags().BoolVar(©VaultPasswordToClipboardFlag, "vault", false, "Copy the vault password to the clipboard (optional)") return authCmd } diff --git a/cmd/vultr/create.go b/cmd/vultr/create.go index 9cd91113..26a4233d 100644 --- a/cmd/vultr/create.go +++ b/cmd/vultr/create.go @@ -7,6 +7,7 @@ See the LICENSE file for more details. package vultr import ( + "errors" "fmt" "os" "strings" @@ -30,23 +31,26 @@ func createVultr(cmd *cobra.Command, args []string) error { cliFlags, err := utilities.GetFlags(cmd, "vultr") if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to get flags: %w", err) } progress.DisplayLogHints(15) isValid, catalogApps, err := catalog.ValidateCatalogApps(cliFlags.InstallCatalogApps) + if err != nil { + return fmt.Errorf("catalog apps validation failed: %w", err) + } + if !isValid { - return err + return errors.New("catalog validation failed") } err = ValidateProvidedFlags(cliFlags.GitProvider) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("invalid provided flags: %w", err) } - // If cluster setup is complete, return clusterSetupComplete := viper.GetBool("kubefirst-checks.cluster-install-complete") if clusterSetupComplete { err = fmt.Errorf("this cluster install process has already completed successfully") @@ -59,10 +63,9 @@ func createVultr(cmd *cobra.Command, args []string) error { gitAuth, err := gitShim.ValidateGitCredentials(cliFlags.GitProvider, cliFlags.GithubOrg, cliFlags.GitlabGroup) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to validate git credentials: %w", err) } - // Validate git executionControl := viper.GetBool(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider)) if !executionControl { newRepositoryNames := []string{"gitops", "metaphor"} @@ -79,11 +82,13 @@ func createVultr(cmd *cobra.Command, args []string) error { err = gitShim.InitializeGitProvider(&initGitParameters) if err != nil { progress.Error(err.Error()) - return nil + return fmt.Errorf("failed to initialize git provider: %w", err) } } viper.Set(fmt.Sprintf("kubefirst-checks.%s-credentials", cliFlags.GitProvider), true) - viper.WriteConfig() + if err := viper.WriteConfig(); err != nil { + return fmt.Errorf("failed to write config: %w", err) + } k3dClusterCreationComplete := viper.GetBool("launch.deployed") isK1Debug := strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" @@ -95,10 +100,10 @@ func createVultr(cmd *cobra.Command, args []string) error { err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") + return fmt.Errorf("kubefirst api availability check failed: %w", err) } provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) - return nil } @@ -109,7 +114,6 @@ func ValidateProvidedFlags(gitProvider string) error { return fmt.Errorf("your VULTR_API_KEY variable is unset - please set it before continuing") } - // Validate required environment variables for dns provider if dnsProviderFlag == "cloudflare" { if os.Getenv("CF_API_TOKEN") == "" { return fmt.Errorf("your CF_API_TOKEN environment variable is not set. Please set and try again") @@ -120,20 +124,17 @@ func ValidateProvidedFlags(gitProvider string) error { case "github": key, err := internalssh.GetHostKey("github.com") if err != nil { - return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "github.com", key.Type()) + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan github.com >> ~/.ssh/known_hosts` to remedy: %w", err) } + log.Info().Msgf("%q %s", "github.com", key.Type()) case "gitlab": key, err := internalssh.GetHostKey("gitlab.com") if err != nil { - return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy") - } else { - log.Info().Msgf("%s %s\n", "gitlab.com", key.Type()) + return fmt.Errorf("known_hosts file does not exist - please run `ssh-keyscan gitlab.com >> ~/.ssh/known_hosts` to remedy: %w", err) } + log.Info().Msgf("%q %s", "gitlab.com", key.Type()) } progress.CompleteStep("Validate provided flags") - return nil } From f6fbb6d6206a6a7a96c13c021109047663564c58 Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:29:28 -0400 Subject: [PATCH 2/9] Enable golangci-lint --- .github/workflows/check-unit-tests.yml | 10 +++++++--- cmd/letsencrypt.go | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check-unit-tests.yml b/.github/workflows/check-unit-tests.yml index e0a90e9a..2fd99120 100644 --- a/.github/workflows/check-unit-tests.yml +++ b/.github/workflows/check-unit-tests.yml @@ -11,6 +11,10 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version-file: "go.mod" - - name: Running Golang tests - run: go test -v -short ./... + go-version-file: go.mod + - name: Run GolangCI-Lint + uses: golangci/golangci-lint-action@v6 + with: + version: v1.60.3 + - name: Test application + run: go test -short -v ./... diff --git a/cmd/letsencrypt.go b/cmd/letsencrypt.go index ea21f05e..1075338c 100644 --- a/cmd/letsencrypt.go +++ b/cmd/letsencrypt.go @@ -7,7 +7,7 @@ See the LICENSE file for more details. package cmd import ( - "log" + "fmt" "github.com/konstructio/kubefirst-api/pkg/certificates" "github.com/konstructio/kubefirst/internal/progress" @@ -37,7 +37,7 @@ func status() *cobra.Command { TraverseChildren: true, Run: func(cmd *cobra.Command, args []string) { if err := certificates.CheckCertificateUsage(domainNameFlag); err != nil { - log.Printf("failed to check certificate usage for domain %q: %w", domainNameFlag, err) + fmt.Printf("failed to check certificate usage for domain %q: %s\n", domainNameFlag, err) } progress.Progress.Quit() }, From 8b2fb56eaaa282be19d42e62b85849bafefd7845 Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:30:08 -0400 Subject: [PATCH 3/9] Fix version printing. --- cmd/version.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/version.go b/cmd/version.go index 3d57f95d..4b5e6775 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -7,8 +7,6 @@ See the LICENSE file for more details. package cmd import ( - "fmt" - "github.com/konstructio/kubefirst-api/pkg/configs" "github.com/konstructio/kubefirst/internal/progress" "github.com/spf13/cobra" @@ -25,7 +23,7 @@ var versionCmd = &cobra.Command{ Run: func(cmd *cobra.Command, _ []string) { versionMsg := ` ## -### kubefirst-cli golang utility version:` + fmt.Sprintf("%s", configs.K1Version) +### kubefirst-cli golang utility version: ` + configs.K1Version progress.Success(versionMsg) }, From b30c8798636ee71d250c2b913e64a7400c08fafe Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:06:16 -0400 Subject: [PATCH 4/9] Cleanup internal folder --- internal/catalog/catalog.go | 36 ++++----- internal/cluster/cluster.go | 98 +++++++++++------------ internal/common/common.go | 24 +++--- internal/gitShim/containerRegistryAuth.go | 17 ++-- internal/gitShim/init.go | 73 ++++++++--------- internal/k3d/menu.go | 23 +++--- internal/launch/cmd.go | 95 ++++++++++++++-------- internal/launch/utils.go | 15 ++-- internal/progress/command.go | 3 + internal/progress/constants.go | 8 +- internal/progress/message.go | 31 +++---- internal/progress/progress.go | 1 - internal/progress/styles.go | 4 +- internal/progress/types.go | 2 +- internal/provision/provision.go | 14 +++- internal/provisionLogs/command.go | 3 +- internal/provisionLogs/message.go | 6 +- internal/provisionLogs/provisionLogs.go | 2 +- internal/provisionLogs/types.go | 2 +- internal/segment/segment.go | 10 ++- internal/utilities/flags.go | 56 ++++++------- internal/utilities/utilities.go | 50 +++++++----- 22 files changed, 301 insertions(+), 272 deletions(-) diff --git a/internal/catalog/catalog.go b/internal/catalog/catalog.go index 0fcd5473..90290bb0 100644 --- a/internal/catalog/catalog.go +++ b/internal/catalog/catalog.go @@ -43,19 +43,19 @@ func ReadActiveApplications() (apiTypes.GitopsCatalogApps, error) { activeContent, err := gh.ReadGitopsCatalogRepoContents() if err != nil { - return apiTypes.GitopsCatalogApps{}, fmt.Errorf("error retrieving gitops catalog repository content: %s", err) + return apiTypes.GitopsCatalogApps{}, fmt.Errorf("error retrieving gitops catalog repository content: %w", err) } index, err := gh.ReadGitopsCatalogIndex(activeContent) if err != nil { - return apiTypes.GitopsCatalogApps{}, fmt.Errorf("error retrieving gitops catalog index content: %s", err) + return apiTypes.GitopsCatalogApps{}, fmt.Errorf("error retrieving gitops catalog index content: %w", err) } var out apiTypes.GitopsCatalogApps err = yaml.Unmarshal(index, &out) if err != nil { - return apiTypes.GitopsCatalogApps{}, fmt.Errorf("error retrieving gitops catalog applications: %s", err) + return apiTypes.GitopsCatalogApps{}, fmt.Errorf("error retrieving gitops catalog applications: %w", err) } return out, nil @@ -71,7 +71,7 @@ func ValidateCatalogApps(catalogApps string) (bool, []apiTypes.GitopsCatalogApp, apps, err := ReadActiveApplications() if err != nil { - log.Error().Msgf(fmt.Sprintf("Error getting gitops catalag applications: %s", err)) + log.Error().Msgf("error getting gitops catalog applications: %s", err) return false, gitopsCatalogapps, err } @@ -86,7 +86,7 @@ func ValidateCatalogApps(catalogApps string) (bool, []apiTypes.GitopsCatalogApp, secretValue := os.Getenv(secret.Env) if secretValue == "" { - return false, gitopsCatalogapps, fmt.Errorf("your %s environment variable is not set for %s catalog application. Please set and try again", secret.Env, app) + return false, gitopsCatalogapps, fmt.Errorf("your %q environment variable is not set for %q catalog application. Please set and try again", secret.Env, app) } secret.Value = secretValue @@ -97,7 +97,7 @@ func ValidateCatalogApps(catalogApps string) (bool, []apiTypes.GitopsCatalogApp, for _, config := range catalogApp.ConfigKeys { configValue := os.Getenv(config.Env) if configValue == "" { - return false, gitopsCatalogapps, fmt.Errorf("your %s environment variable is not set for %s catalog application. Please set and try again", config.Env, app) + return false, gitopsCatalogapps, fmt.Errorf("your %q environment variable is not set for %q catalog application. Please set and try again", config.Env, app) } config.Value = configValue } @@ -109,7 +109,7 @@ func ValidateCatalogApps(catalogApps string) (bool, []apiTypes.GitopsCatalogApp, } } if !found { - return false, gitopsCatalogapps, fmt.Errorf(fmt.Sprintf("catalag app is not supported: %s", app)) + return false, gitopsCatalogapps, fmt.Errorf("catalog app is not supported: %q", app) } } @@ -125,7 +125,7 @@ func (gh *GitHubClient) ReadGitopsCatalogRepoContents() ([]*git.RepositoryConten nil, ) if err != nil { - return nil, err + return nil, fmt.Errorf("error retrieving gitops catalog repository contents: %w", err) } return directoryContent, nil @@ -134,20 +134,16 @@ func (gh *GitHubClient) ReadGitopsCatalogRepoContents() ([]*git.RepositoryConten // ReadGitopsCatalogIndex reads the gitops catalog repository index func (gh *GitHubClient) ReadGitopsCatalogIndex(contents []*git.RepositoryContent) ([]byte, error) { for _, content := range contents { - switch *content.Type { - case "file": - switch *content.Name { - case "index.yaml": - b, err := gh.readFileContents(content) - if err != nil { - return b, err - } - return b, nil + if *content.Type == "file" && *content.Name == "index.yaml" { + b, err := gh.readFileContents(content) + if err != nil { + return nil, fmt.Errorf("error reading index.yaml file: %w", err) } + return b, nil } } - return []byte{}, fmt.Errorf("index.yaml not found in gitops catalog repository") + return nil, fmt.Errorf("index.yaml not found in gitops catalog repository") } // readFileContents parses the contents of a file in a GitHub repository @@ -160,13 +156,13 @@ func (gh *GitHubClient) readFileContents(content *git.RepositoryContent) ([]byte nil, ) if err != nil { - return []byte{}, err + return nil, fmt.Errorf("error downloading contents of %q: %w", *content.Path, err) } defer rc.Close() b, err := io.ReadAll(rc) if err != nil { - return []byte{}, err + return nil, fmt.Errorf("error reading contents of %q: %w", *content.Path, err) } return b, nil diff --git a/internal/cluster/cluster.go b/internal/cluster/cluster.go index da7457b2..f34b2251 100644 --- a/internal/cluster/cluster.go +++ b/internal/cluster/cluster.go @@ -40,37 +40,36 @@ func CreateCluster(cluster apiTypes.ClusterDefinition) error { payload, err := json.Marshal(requestObject) if err != nil { - return err + return fmt.Errorf("failed to marshal request object: %w", err) } req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngresUrl()), bytes.NewReader(payload)) if err != nil { - log.Info().Msgf("error %s", err) - return err + log.Printf("error creating request: %s", err) + return fmt.Errorf("failed to create request: %w", err) } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") res, err := httpClient.Do(req) if err != nil { - log.Info().Msgf("error %s", err) - return err + log.Printf("error executing request: %s", err) + return fmt.Errorf("failed to execute request: %w", err) } defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { - log.Info().Msgf("unable to create cluster %s", err) - - return err + log.Printf("unable to create cluster: %v", err) + return fmt.Errorf("failed to read response body: %w", err) } if res.StatusCode != http.StatusAccepted { - log.Info().Msgf("unable to create cluster %s %s", res.Status, body) - return fmt.Errorf("unable to create cluster %s %s", res.Status, body) + log.Printf("unable to create cluster: %q %q", res.Status, body) + return fmt.Errorf("unable to create cluster: API returned unexpected status code %q: %s", res.Status, body) } - log.Info().Msgf("Created cluster: %s", string(body)) + log.Printf("Created cluster: %q", string(body)) return nil } @@ -85,38 +84,36 @@ func ResetClusterProgress(clusterName string) error { payload, err := json.Marshal(requestObject) if err != nil { - return err + return fmt.Errorf("failed to marshal request object: %w", err) } req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngresUrl()), bytes.NewReader(payload)) if err != nil { - log.Info().Msgf("error %s", err) - return err + log.Printf("error creating request: %v", err) + return fmt.Errorf("failed to create request: %w", err) } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") res, err := httpClient.Do(req) if err != nil { - log.Info().Msgf("error %s", err) - return err + log.Printf("error executing request: %v", err) + return fmt.Errorf("failed to execute request: %w", err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { - log.Info().Msgf("unable to create cluster %s", res.Status) - return err + log.Printf("unable to reset cluster progress: %q", res.Status) + return fmt.Errorf("unable to reset cluster progress: API returned unexpected status %q", res.Status) } body, err := io.ReadAll(res.Body) if err != nil { - log.Info().Msgf("unable to create cluster %s", err) - - return err + log.Printf("unable to read response body: %v", err) + return fmt.Errorf("failed to read response body: %w", err) } log.Info().Msgf("Import: %s", string(body)) - return nil } @@ -127,34 +124,34 @@ func GetCluster(clusterName string) (apiTypes.Cluster, error) { cluster := apiTypes.Cluster{} req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngresUrl(), clusterName), nil) if err != nil { - log.Info().Msgf("error %s", err) - return cluster, err + log.Printf("error creating request: %v", err) + return cluster, fmt.Errorf("failed to create request: %w", err) } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") res, err := httpClient.Do(req) if err != nil { - log.Info().Msgf("error %s", err) - return cluster, err + log.Printf("error executing request: %v", err) + return cluster, fmt.Errorf("failed to execute request: %w", err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { - log.Info().Msgf("unable to get cluster %s, continuing", res.Status) - return cluster, err + log.Printf("unable to get cluster: %q, continuing", res.Status) + return cluster, fmt.Errorf("unable to get cluster: %q", res.Status) } body, err := io.ReadAll(res.Body) if err != nil { - log.Info().Msgf("unable to get cluster %s", err) - return cluster, err + log.Printf("unable to read response body: %v", err) + return cluster, fmt.Errorf("failed to read response body: %w", err) } err = json.Unmarshal(body, &cluster) if err != nil { - log.Info().Msgf("unable to cast cluster object %s", err) - return cluster, err + log.Printf("unable to unmarshal cluster object: %v", err) + return cluster, fmt.Errorf("failed to unmarshal cluster object: %w", err) } return cluster, nil @@ -167,33 +164,34 @@ func GetClusters() ([]apiTypes.Cluster, error) { clusters := []apiTypes.Cluster{} req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster", GetConsoleIngresUrl()), nil) if err != nil { - log.Info().Msgf("error %s", err) - return clusters, err + log.Printf("error creating request: %v", err) + return clusters, fmt.Errorf("failed to create request: %w", err) } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") res, err := httpClient.Do(req) if err != nil { - log.Info().Msgf("error %s", err) - return clusters, err + log.Printf("error executing request: %v", err) + return clusters, fmt.Errorf("failed to execute request: %w", err) } + defer res.Body.Close() if res.StatusCode != http.StatusOK { - log.Info().Msgf("unable to get clusters %s, continuing", res.Status) - return clusters, err + log.Printf("unable to get clusters: %q", res.Status) + return clusters, fmt.Errorf("unable to get clusters: API returned unexpected status code %q", res.Status) } body, err := io.ReadAll(res.Body) if err != nil { - log.Info().Msgf("unable to get clusters %s", err) - return clusters, err + log.Printf("unable to read response body: %v", err) + return clusters, fmt.Errorf("failed to read response body: %w", err) } err = json.Unmarshal(body, &clusters) if err != nil { - log.Info().Msgf("unable to cast clusters object %s", err) - return clusters, err + log.Printf("unable to unmarshal clusters object: %v", err) + return clusters, fmt.Errorf("failed to unmarshal clusters object: %w", err) } return clusters, nil @@ -205,27 +203,27 @@ func DeleteCluster(clusterName string) error { req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngresUrl(), clusterName), nil) if err != nil { - log.Info().Msgf("error %s", err) - return err + log.Printf("error creating request: %v", err) + return fmt.Errorf("failed to create request: %w", err) } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") res, err := httpClient.Do(req) if err != nil { - log.Info().Msgf("error %s", err) - return err + log.Printf("error executing request: %v", err) + return fmt.Errorf("failed to execute request: %w", err) } if res.StatusCode != http.StatusOK { - log.Info().Msgf("unable to delete cluster %s, continuing", res.Status) - return err + log.Printf("unable to delete cluster: %q, continuing", res.Status) + return fmt.Errorf("unable to delete cluster: API returned unexpected status code %q", res.Status) } _, err = io.ReadAll(res.Body) if err != nil { - log.Info().Msgf("unable to delete cluster %s", err) - return err + log.Printf("unable to read response body: %v", err) + return fmt.Errorf("failed to read response body: %w", err) } return nil diff --git a/internal/common/common.go b/internal/common/common.go index f019cb5c..be94af31 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -30,7 +30,7 @@ type CheckResponse struct { // Current is current latest version on source. Current string - // Outdate is true when target version is less than Curernt on source. + // Outdate is true when target version is less than Current on source. Outdated bool // Latest is true when target version is equal to Current on source. @@ -88,6 +88,10 @@ func versionCheck() (res *CheckResponse, skip bool) { re := regexp.MustCompile(`.*/v(.*).tar.gz"`) matches := re.FindStringSubmatch(bodyString) + if len(matches) < 2 { + fmt.Println("checking for a newer version failed (no version match)") + return nil, true + } latestVersion = matches[1] return &CheckResponse{ @@ -98,13 +102,13 @@ func versionCheck() (res *CheckResponse, skip bool) { }, false } -func GetRootCredentials(cmd *cobra.Command, args []string) error { +func GetRootCredentials(_ *cobra.Command, _ []string) error { clusterName := viper.GetString("flags.cluster-name") cluster, err := cluster.GetCluster(clusterName) if err != nil { progress.Error(err.Error()) - return err + return fmt.Errorf("failed to get cluster: %w", err) } progress.DisplayCredentials(cluster) @@ -113,7 +117,7 @@ func GetRootCredentials(cmd *cobra.Command, args []string) error { } func Destroy(cmd *cobra.Command, args []string) error { - // Determine if there are active instal ls + // Determine if there are active installs gitProvider := viper.GetString("flags.git-provider") gitProtocol := viper.GetString("flags.git-protocol") cloudProvider := viper.GetString("kubefirst.cloud-provider") @@ -132,6 +136,7 @@ func Destroy(cmd *cobra.Command, args []string) error { cGitOwner = viper.GetString("flags.gitlab-owner") default: progress.Error("invalid git provider option") + return fmt.Errorf("invalid git provider: %q", gitProvider) } // Instantiate aws config @@ -162,13 +167,14 @@ func Destroy(cmd *cobra.Command, args []string) error { viper.Set("kubefirst", "") viper.Set("flags", "") viper.Set("k1-paths", "") - viper.WriteConfig() + if err := viper.WriteConfig(); err != nil { + return fmt.Errorf("failed to write config: %w", err) + } if _, err := os.Stat(config.K1Dir + "/kubeconfig"); !os.IsNotExist(err) { - err = os.Remove(config.K1Dir + "/kubeconfig") - if err != nil { + if err := os.Remove(config.K1Dir + "/kubeconfig"); err != nil { progress.Error(fmt.Sprintf("unable to delete %q folder, error: %s", config.K1Dir+"/kubeconfig", err)) - return err + return fmt.Errorf("unable to delete kubeconfig: %w", err) } } @@ -188,7 +194,7 @@ https://docs.kubefirst.io/` + cloudProvider + `/deprovision } // checkDocker makes sure Docker is running before all commands -func CheckDocker(cmd *cobra.Command, args []string) { +func CheckDocker(_ *cobra.Command, _ []string) { // Verify Docker is running dcli := docker.DockerClientWrapper{ Client: docker.NewDockerClient(), diff --git a/internal/gitShim/containerRegistryAuth.go b/internal/gitShim/containerRegistryAuth.go index 0469428a..f497a3e8 100644 --- a/internal/gitShim/containerRegistryAuth.go +++ b/internal/gitShim/containerRegistryAuth.go @@ -12,7 +12,6 @@ import ( "github.com/konstructio/kubefirst-api/pkg/gitlab" "github.com/konstructio/kubefirst-api/pkg/k8s" - "github.com/rs/zerolog/log" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -42,7 +41,7 @@ func CreateContainerRegistrySecret(obj *ContainerRegistryAuth) (string, error) { case "github": usernamePasswordString := fmt.Sprintf("%s:%s", obj.GitUser, obj.GitToken) usernamePasswordStringB64 := base64.StdEncoding.EncodeToString([]byte(usernamePasswordString)) - dockerConfigString := fmt.Sprintf(`{"auths": {"%s": {"username": "%s", "password": "%s", "email": "%s", "auth": "%s"}}}`, + dockerConfigString := fmt.Sprintf(`{"auths": {"%s": {"username": %q, "password": %q, "email": %q, "auth": %q}}}`, obj.ContainerRegistryHost, obj.GithubOwner, obj.GitToken, @@ -50,27 +49,21 @@ func CreateContainerRegistrySecret(obj *ContainerRegistryAuth) (string, error) { usernamePasswordStringB64, ) - // Create argo workflows pull secret argoDeployTokenSecret := &v1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: secretName, Namespace: "argo"}, Data: map[string][]byte{"config.json": []byte(dockerConfigString)}, Type: "Opaque", } - err := k8s.CreateSecretV2(obj.Clientset, argoDeployTokenSecret) - if err != nil { - log.Error().Msgf("error while creating secret for container registry auth: %s", err) + if err := k8s.CreateSecretV2(obj.Clientset, argoDeployTokenSecret); err != nil { + return "", fmt.Errorf("error while creating secret for GitHub container registry auth: %w", err) } - // GitLab Deploy Tokens - // Project deploy tokens are generated for each member of createTokensForProjects - // These deploy tokens are used to authorize against the GitLab container registry case "gitlab": gitlabClient, err := gitlab.NewGitLabClient(obj.GitToken, obj.GitlabGroupFlag) if err != nil { - return "", err + return "", fmt.Errorf("error while creating GitLab client: %w", err) } - // Create argo workflows pull secret p := gitlab.DeployTokenCreateParameters{ Name: secretName, Username: secretName, @@ -78,7 +71,7 @@ func CreateContainerRegistrySecret(obj *ContainerRegistryAuth) (string, error) { } token, err := gitlabClient.CreateGroupDeployToken(0, &p) if err != nil { - log.Error().Msgf("error while creating secret for container registry auth: %s", err) + return "", fmt.Errorf("error while creating GitLab group deploy token: %w", err) } return token, nil diff --git a/internal/gitShim/init.go b/internal/gitShim/init.go index 2819384a..124bffe7 100644 --- a/internal/gitShim/init.go +++ b/internal/gitShim/init.go @@ -7,6 +7,7 @@ See the LICENSE file for more details. package gitShim import ( + "errors" "fmt" "net/http" "os" @@ -42,30 +43,28 @@ func InitializeGitProvider(p *GitInitParameters) error { case "github": githubSession := github.New(p.GitToken) newRepositoryExists := false - // todo hoist to globals - errorMsg := "the following repositories must be removed before continuing with your kubefirst installation.\n\t" + errorMsg := "the following repositories must be removed before continuing with your Kubefirst installation.\n\t" for _, repositoryName := range p.Repositories { responseStatusCode := githubSession.CheckRepoExists(p.GitOwner, repositoryName) - // https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#get-a-repository repositoryExistsStatusCode := 200 repositoryDoesNotExistStatusCode := 404 if responseStatusCode == repositoryExistsStatusCode { - log.Info().Msgf("repository https://github.com/%s/%s exists", p.GitOwner, repositoryName) - errorMsg = errorMsg + fmt.Sprintf("https://github.com/%s/%s\n\t", p.GitOwner, repositoryName) + log.Info().Msgf("repository %q exists", fmt.Sprintf("https://github.com/%s/%s", p.GitOwner, repositoryName)) + errorMsg += fmt.Sprintf("https://github.com/%s/%s\n\t", p.GitOwner, repositoryName) newRepositoryExists = true } else if responseStatusCode == repositoryDoesNotExistStatusCode { - log.Info().Msgf("repository https://github.com/%s/%s does not exist, continuing", p.GitOwner, repositoryName) + log.Info().Msgf("repository %q does not exist, continuing", fmt.Sprintf("https://github.com/%s/%s", p.GitOwner, repositoryName)) } } if newRepositoryExists { - return fmt.Errorf(errorMsg) + return errors.New(errorMsg) } newTeamExists := false - errorMsg = "the following teams must be removed before continuing with your kubefirst installation.\n\t" + errorMsg = "the following teams must be removed before continuing with your Kubefirst installation.\n\t" for _, teamName := range p.Teams { responseStatusCode := githubSession.CheckTeamExists(p.GitOwner, teamName) @@ -75,45 +74,42 @@ func InitializeGitProvider(p *GitInitParameters) error { teamDoesNotExistStatusCode := 404 if responseStatusCode == teamExistsStatusCode { - log.Info().Msgf("team https://github.com/%s/%s exists", p.GitOwner, teamName) - errorMsg = errorMsg + fmt.Sprintf("https://github.com/orgs/%s/teams/%s\n\t", p.GitOwner, teamName) + log.Info().Msgf("team %q exists", fmt.Sprintf("https://github.com/%s/%s", p.GitOwner, teamName)) + errorMsg += fmt.Sprintf("https://github.com/orgs/%s/teams/%s\n\t", p.GitOwner, teamName) newTeamExists = true } else if responseStatusCode == teamDoesNotExistStatusCode { - log.Info().Msgf("https://github.com/orgs/%s/teams/%s does not exist, continuing", p.GitOwner, teamName) + log.Info().Msgf("team %q does not exist, continuing", fmt.Sprintf("https://github.com/orgs/%s/teams/%s", p.GitOwner, teamName)) } } if newTeamExists { - return fmt.Errorf(errorMsg) + return errors.New(errorMsg) } case "gitlab": gitlabClient, err := gitlab.NewGitLabClient(p.GitToken, p.GitOwner) if err != nil { - return err + return fmt.Errorf("error creating GitLab client: %w", err) } - // Check for existing base projects projects, err := gitlabClient.GetProjects() if err != nil { - log.Fatal().Msgf("couldn't get gitlab projects: %s", err) + return fmt.Errorf("couldn't get GitLab projects: %w", err) } for _, repositoryName := range p.Repositories { for _, project := range projects { if project.Name == repositoryName { - return fmt.Errorf("project %s already exists and will need to be deleted before continuing", repositoryName) + return fmt.Errorf("project %q already exists and will need to be deleted before continuing", repositoryName) } } } - // Check for existing base projects - // Save for detokenize subgroups, err := gitlabClient.GetSubGroups() if err != nil { - log.Fatal().Msgf("couldn't get gitlab subgroups for group %s: %s", p.GitOwner, err) + return fmt.Errorf("couldn't get GitLab subgroups for group %q: %w", p.GitOwner, err) } - for _, teamName := range p.Repositories { + for _, teamName := range p.Teams { for _, sg := range subgroups { if sg.Name == teamName { - return fmt.Errorf("subgroup %s already exists and will need to be deleted before continuing", teamName) + return fmt.Errorf("subgroup %q already exists and will need to be deleted before continuing", teamName) } } } @@ -126,15 +122,14 @@ func InitializeGitProvider(p *GitInitParameters) error { return nil } -func ValidateGitCredentials(gitProviderFlag string, githubOrgFlag string, gitlabGroupFlag string) (types.GitAuth, error) { +func ValidateGitCredentials(gitProviderFlag, githubOrgFlag, gitlabGroupFlag string) (types.GitAuth, error) { progress.AddStep("Validate git credentials") gitAuth := types.GitAuth{} - // Switch based on git provider, set params switch gitProviderFlag { case "github": if githubOrgFlag == "" { - return gitAuth, fmt.Errorf("please provide a github organization using the --github-org flag") + return gitAuth, fmt.Errorf("please provide a GitHub organization using the --github-org flag") } if os.Getenv("GITHUB_TOKEN") == "" { return gitAuth, fmt.Errorf("your GITHUB_TOKEN is not set. Please set and try again") @@ -143,65 +138,60 @@ func ValidateGitCredentials(gitProviderFlag string, githubOrgFlag string, gitlab gitAuth.Owner = githubOrgFlag gitAuth.Token = os.Getenv("GITHUB_TOKEN") - // Verify token scopes err := github.VerifyTokenPermissions(gitAuth.Token) if err != nil { - return gitAuth, err + return gitAuth, fmt.Errorf("error verifying GitHub token permissions: %w", err) } - // Handle authorization checks httpClient := http.DefaultClient gitHubService := services.NewGitHubService(httpClient) gitHubHandler := handlers.NewGitHubHandler(gitHubService) - // get github data to set user based on the provided token - log.Info().Msg("verifying github authentication") + log.Info().Msg("verifying GitHub authentication") githubUser, err := gitHubHandler.GetGitHubUser(gitAuth.Token) if err != nil { - return gitAuth, err + return gitAuth, fmt.Errorf("error getting GitHub user: %w", err) } gitAuth.User = githubUser viper.Set("github.user", githubUser) err = viper.WriteConfig() if err != nil { - return gitAuth, err + return gitAuth, fmt.Errorf("error writing GitHub config: %w", err) } err = gitHubHandler.CheckGithubOrganizationPermissions(gitAuth.Token, githubOrgFlag, githubUser) if err != nil { - return gitAuth, err + return gitAuth, fmt.Errorf("error checking GitHub organization permissions: %w", err) } viper.Set("flags.github-owner", githubOrgFlag) viper.WriteConfig() case "gitlab": if gitlabGroupFlag == "" { - return gitAuth, fmt.Errorf("please provide a gitlab group using the --gitlab-group flag") + return gitAuth, fmt.Errorf("please provide a GitLab group using the --gitlab-group flag") } if os.Getenv("GITLAB_TOKEN") == "" { - return gitAuth, fmt.Errorf("your GITLAB_TOKEN is not set. please set and try again") + return gitAuth, fmt.Errorf("your GITLAB_TOKEN is not set. Please set and try again") } gitAuth.Token = os.Getenv("GITLAB_TOKEN") - // Verify token scopes err := gitlab.VerifyTokenPermissions(gitAuth.Token) if err != nil { - return gitAuth, err + return gitAuth, fmt.Errorf("error verifying GitLab token permissions: %w", err) } gitlabClient, err := gitlab.NewGitLabClient(gitAuth.Token, gitlabGroupFlag) if err != nil { - return gitAuth, err + return gitAuth, fmt.Errorf("error creating GitLab client: %w", err) } gitAuth.Owner = gitlabClient.ParentGroupPath cGitlabOwnerGroupID := gitlabClient.ParentGroupID - log.Info().Msgf("set gitlab owner to %s", gitAuth.Owner) + log.Info().Msgf("set GitLab owner to %q", gitAuth.Owner) - // Get authenticated user's name user, _, err := gitlabClient.Client.Users.CurrentUser() if err != nil { - return gitAuth, fmt.Errorf("unable to get authenticated user info - please make sure GITLAB_TOKEN env var is set %s", err) + return gitAuth, fmt.Errorf("unable to get authenticated user info - please make sure GITLAB_TOKEN env var is set: %w", err) } gitAuth.User = user.Username @@ -209,7 +199,8 @@ func ValidateGitCredentials(gitProviderFlag string, githubOrgFlag string, gitlab viper.Set("flags.gitlab-owner-group-id", cGitlabOwnerGroupID) viper.WriteConfig() default: - log.Error().Msgf("invalid git provider option") + log.Printf("invalid git provider option: %q", gitProviderFlag) + return gitAuth, fmt.Errorf("invalid git provider: %q", gitProviderFlag) } progress.CompleteStep("Validate git credentials") diff --git a/internal/k3d/menu.go b/internal/k3d/menu.go index 668d8461..76ccc29c 100644 --- a/internal/k3d/menu.go +++ b/internal/k3d/menu.go @@ -9,12 +9,12 @@ package k3d import ( "fmt" "io" + "log" "strings" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/konstructio/kubefirst/internal/progress" ) const ( @@ -104,9 +104,9 @@ func (m Model) View() string { return "\n" + m.List.View() } -func MongoDestinationChooser(inCluster bool) string { +func MongoDestinationChooser(inCluster bool) (result string, err error) { if inCluster { - return "in-cluster" + return "in-cluster", nil } items := []list.Item{ @@ -126,18 +126,15 @@ func MongoDestinationChooser(inCluster bool) string { model, err := tea.NewProgram(m).Run() if err != nil { - fmt.Println("Error running program:", err) - progress.Progress.Quit() + log.Printf("Error running program: %v", err) + return "", fmt.Errorf("failed to run the program: %w", err) } - var result string - if strings.Contains(model.View(), "atlas") { - result = "atlas" - } else if strings.Contains(model.View(), "in-cluster") { - result = "in-cluster" - } else { - result = "error" + return "atlas", nil + } + if strings.Contains(model.View(), "in-cluster") { + return "in-cluster", nil } - return result + return "error", nil } diff --git a/internal/launch/cmd.go b/internal/launch/cmd.go index ece9fe31..41661cf0 100644 --- a/internal/launch/cmd.go +++ b/internal/launch/cmd.go @@ -35,7 +35,7 @@ import ( var consoleClusterName = "kubefirst-console" // Up -func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { +func Up(additionalHelmFlags []string, inCluster, useTelemetry bool) { if viper.GetBool("launch.deployed") { progress.Error("Kubefirst console has already been deployed. To start over, run `kubefirst launch down` to completely remove the existing console.") } @@ -46,20 +46,21 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { homeDir, err := os.UserHomeDir() if err != nil { - progress.Error(fmt.Sprintf("something went wrong getting home path: %s", err)) + progress.Error(fmt.Sprintf("unable to get user's home directory: %s", err)) + return } dir := fmt.Sprintf("%s/.k1/%s", homeDir, consoleClusterName) if _, err := os.Stat(dir); os.IsNotExist(err) { err := os.MkdirAll(dir, os.ModePerm) if err != nil { - log.Info().Msgf("%s directory already exists, continuing", dir) + log.Info().Msgf("%q directory already exists, continuing", dir) } } toolsDir := fmt.Sprintf("%s/tools", dir) if _, err := os.Stat(toolsDir); os.IsNotExist(err) { err := os.MkdirAll(toolsDir, os.ModePerm) if err != nil { - log.Info().Msgf("%s directory already exists, continuing", toolsDir) + log.Info().Msgf("%q directory already exists, continuing", toolsDir) } } @@ -81,13 +82,15 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { err = downloadManager.DownloadFile(k3dClient, k3dDownloadUrl) if err != nil { progress.Error(fmt.Sprintf("error while trying to download k3d: %s", err)) + return } err = os.Chmod(k3dClient, 0o755) if err != nil { - log.Fatal().Msg(err.Error()) + progress.Error(fmt.Sprintf("error changing permissions of k3d client: %s", err)) + return } } else { - log.Info().Msgf("k3d is already installed, continuing") + log.Info().Msg("k3d is already installed, continuing") } progress.CompleteStep("Download k3d") @@ -107,10 +110,12 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { err = downloadManager.DownloadFile(helmDownloadTarGzPath, helmDownloadUrl) if err != nil { progress.Error(fmt.Sprintf("error while trying to download helm: %s", err)) + return } helmTarDownload, err := os.Open(helmDownloadTarGzPath) if err != nil { - progress.Error("could not read helm download content") + progress.Error(fmt.Sprintf("could not read helm download content: %s", err)) + return } downloadManager.ExtractFileFromTarGz( helmTarDownload, @@ -119,7 +124,8 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { ) err = os.Chmod(helmClient, 0o755) if err != nil { - log.Fatal().Msg(err.Error()) + progress.Error(fmt.Sprintf("error changing permissions of helm client: %s", err)) + return } os.Remove(helmDownloadTarGzPath) } else { @@ -141,10 +147,12 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { err = downloadManager.DownloadFile(mkcertClient, mkcertDownloadURL) if err != nil { progress.Error(fmt.Sprintf("error while trying to download mkcert: %s", err)) + return } err = os.Chmod(mkcertClient, 0o755) if err != nil { - progress.Error(err.Error()) + progress.Error(fmt.Sprintf("error changing permissions of mkcert client: %s", err)) + return } } else { log.Info().Msg("mkcert is already installed, continuing") @@ -170,9 +178,10 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { fmt.Sprintf("%s/kubeconfig", dir), ) if err != nil { - msg := fmt.Sprintf("error creating k3d cluster: %s", err) - progress.Error(msg) + progress.Error(fmt.Sprintf("error creating k3d cluster: %s", err)) + return } + log.Info().Msg("k3d cluster for Kubefirst console and API created successfully") // Wait for traefik @@ -187,10 +196,12 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { ) if err != nil { progress.Error(fmt.Sprintf("error looking for traefik: %s", err)) + return } _, err = k8s.WaitForDeploymentReady(kcfg.Clientset, traefikDeployment, 120) if err != nil { progress.Error(fmt.Sprintf("error waiting for traefik: %s", err)) + return } } @@ -208,7 +219,8 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { "yaml", ) if err != nil { - log.Fatal().Msgf("error listing current helm repositories: %s", err) + progress.Error(fmt.Sprintf("error listing current helm repositories: %s", err)) + return } var existingHelmRepositories []helm.HelmRepo @@ -217,7 +229,9 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { err = yaml.Unmarshal([]byte(res), &existingHelmRepositories) if err != nil { progress.Error(fmt.Sprintf("could not get existing helm repositories: %s", err)) + return } + for _, repo := range existingHelmRepositories { if repo.Name == helmChartRepoName && repo.URL == helmChartRepoURL { repoExists = true @@ -234,7 +248,8 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { helmChartRepoURL, ) if err != nil { - log.Error().Msgf("error adding helm chart repository: %s", err) + progress.Error(fmt.Sprintf("error adding helm chart repository: %s", err)) + return } log.Info().Msg("Added Kubefirst helm chart repository") } else { @@ -248,7 +263,8 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { "update", ) if err != nil { - log.Error().Msgf("error updating helm chart repository: %s", err) + progress.Error(fmt.Sprintf("error updating helm chart repository: %s", err)) + return } log.Info().Msg("Kubefirst helm chart repository updated") @@ -263,7 +279,8 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { "-A", ) if err != nil { - log.Error().Msgf("error listing current helm repositories: %s", err) + progress.Error(fmt.Sprintf("error listing current helm releases: %s", err)) + return } var existingHelmReleases []helm.HelmRelease @@ -272,6 +289,7 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { err = yaml.Unmarshal([]byte(res), &existingHelmReleases) if err != nil { progress.Error(fmt.Sprintf("could not get existing helm releases: %s", err)) + return } for _, release := range existingHelmReleases { if release.Name == helmChartName { @@ -341,6 +359,7 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { a, b, err := shell.ExecShellReturnStrings(helmClient, installFlags...) if err != nil { progress.Error(fmt.Sprintf("error installing helm chart: %s %s %s", err, a, b)) + return } log.Info().Msg("Kubefirst console helm chart installed successfully") @@ -363,10 +382,13 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { ) if err != nil { progress.Error(fmt.Sprintf("error looking for kubefirst api: %s", err)) + return } + _, err = k8s.WaitForDeploymentReady(kcfg.Clientset, apiDeployment, 300) if err != nil { progress.Error(fmt.Sprintf("error waiting for kubefirst api: %s", err)) + return } // Generate certificate for console @@ -374,7 +396,8 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { if _, err := os.Stat(sslPemDir); os.IsNotExist(err) { err := os.MkdirAll(sslPemDir, os.ModePerm) if err != nil { - log.Warn().Msgf("%s directory already exists, continuing", sslPemDir) + progress.Error(fmt.Sprintf("error creating directory for certificates: %s", err)) + return } } log.Info().Msg("Certificate directory created") @@ -383,7 +406,8 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { if _, err := os.Stat(mkcertPemDir); os.IsNotExist(err) { err := os.MkdirAll(mkcertPemDir, os.ModePerm) if err != nil { - log.Warn().Msgf("%s directory already exists, continuing", mkcertPemDir) + progress.Error(fmt.Sprintf("error creating directory for certificates: %s", err)) + return } } @@ -402,21 +426,24 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { ) if err != nil { progress.Error(fmt.Sprintf("error generating certificate for console: %s", err)) + return } //* read certificate files certPem, err := os.ReadFile(fmt.Sprintf("%s/%s-cert.pem", mkcertPemDir, "kubefirst-console")) if err != nil { - progress.Error(fmt.Sprintf("error generating certificate for console: %s", err)) + progress.Error(fmt.Sprintf("error reading certificate for console: %s", err)) + return } keyPem, err := os.ReadFile(fmt.Sprintf("%s/%s-key.pem", mkcertPemDir, "kubefirst-console")) if err != nil { - progress.Error(fmt.Sprintf("error generating certificate for console: %s", err)) + progress.Error(fmt.Sprintf("error reading key for console: %s", err)) + return } _, err = kcfg.Clientset.CoreV1().Secrets(namespace).Get(context.Background(), "kubefirst-console-tls", metav1.GetOptions{}) if err == nil { - log.Info().Msg(fmt.Sprintf("kubernetes secret %s/%s already created - skipping", namespace, "kubefirst-console")) + log.Info().Msg(fmt.Sprintf("kubernetes secret %q already created - skipping", "kubefirst-console")) } else if strings.Contains(err.Error(), "not found") { _, err = kcfg.Clientset.CoreV1().Secrets(namespace).Create(context.Background(), &v1.Secret{ Type: "kubernetes.io/tls", @@ -431,6 +458,7 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { }, metav1.CreateOptions{}) if err != nil { progress.Error(fmt.Sprintf("error creating kubernetes secret for cert: %s", err)) + return } time.Sleep(5 * time.Second) log.Info().Msg("Created Kubernetes Secret for certificate") @@ -439,20 +467,19 @@ func Up(additionalHelmFlags []string, inCluster bool, useTelemetry bool) { progress.CompleteStep("Waiting for kubefirst Deployment") if !inCluster { - log.Info().Msg(fmt.Sprintf("Kubefirst Console is now available! %s", consoleURL)) + log.Info().Msg(fmt.Sprintf("Kubefirst Console is now available! %q", consoleURL)) - log.Warn().Msgf("Kubefirst has generated local certificates for use with the console using `mkcert`.") - log.Warn().Msgf("If you experience certificate errors when accessing the console, please run the following command: ") - log.Warn().Msgf(" %s -install", mkcertClient) - log.Warn().Msgf("") - log.Warn().Msgf("For more information on `mkcert`, check out: https://github.com/FiloSottile/mkcert") + log.Warn().Msg("Kubefirst has generated local certificates for use with the console using `mkcert`.") + log.Warn().Msg("If you experience certificate errors when accessing the console, please run the following command:") + log.Warn().Msg(fmt.Sprintf(" %q -install", mkcertClient)) + log.Warn().Msg("For more information on `mkcert`, check out: https://github.com/FiloSottile/mkcert") - log.Info().Msg("To remove Kubefirst Console and the k3d cluster it runs in, please run the following command: ") + log.Info().Msg("To remove Kubefirst Console and the k3d cluster it runs in, please run the following command:") log.Info().Msg("kubefirst launch down") err = pkg.OpenBrowser(consoleURL) if err != nil { - log.Error().Msgf("error attempting to open console in browser: %s", err) + log.Printf("error attempting to open console in browser: %v", err) } } @@ -475,13 +502,15 @@ func Down(inCluster bool) { homeDir, err := os.UserHomeDir() if err != nil { progress.Error(fmt.Sprintf("something went wrong getting home path: %s", err)) + return } log.Info().Msg("Deleting k3d cluster for Kubefirst console and API") dir := fmt.Sprintf("%s/.k1/%s", homeDir, consoleClusterName) if _, err := os.Stat(dir); os.IsNotExist(err) { - progress.Error(fmt.Sprintf("cluster %s directory does not exist", dir)) + progress.Error(fmt.Sprintf("cluster %q directory does not exist", dir)) + return } toolsDir := fmt.Sprintf("%s/tools", dir) k3dClient := fmt.Sprintf("%s/k3d", toolsDir) @@ -489,14 +518,15 @@ func Down(inCluster bool) { _, _, err = shell.ExecShellReturnStrings(k3dClient, "cluster", "delete", consoleClusterName) if err != nil { progress.Error(fmt.Sprintf("error deleting k3d cluster: %s", err)) + return } log.Info().Msg("k3d cluster for Kubefirst console and API deleted successfully") - log.Info().Msg(fmt.Sprintf("Deleting cluster directory at %s", dir)) + log.Info().Msg(fmt.Sprintf("Deleting cluster directory at %q", dir)) err = os.RemoveAll(dir) if err != nil { - log.Warn().Msgf("unable to remove directory at %s", dir) + log.Warn().Msgf("unable to remove directory at %q", dir) } viper.Set("kubefirst", "") @@ -528,7 +558,8 @@ func ListClusters() { func DeleteCluster(managedClusterName string) { err := cluster.DeleteCluster(managedClusterName) if err != nil { - progress.Error(fmt.Sprintf("error: cluster %s not found\n", managedClusterName)) + progress.Error(fmt.Sprintf("error: cluster %q not found", managedClusterName)) + return } deleteMessage := ` diff --git a/internal/launch/utils.go b/internal/launch/utils.go index 1246a94e..f57a0b84 100644 --- a/internal/launch/utils.go +++ b/internal/launch/utils.go @@ -7,7 +7,9 @@ See the LICENSE file for more details. package launch import ( + "bytes" "fmt" + "text/tabwriter" "github.com/konstructio/kubefirst-api/pkg/types" "github.com/konstructio/kubefirst/internal/progress" @@ -16,13 +18,12 @@ import ( // displayFormattedClusterInfo uses tabwriter to pretty print information on clusters using // the specified formatting func displayFormattedClusterInfo(clusters []types.Cluster) { - header := ` -| NAME | CREATED AT | STATUS | TYPE | PROVIDER | -| --- | --- | --- | --- | --- | - ` - content := "" + var buf bytes.Buffer + tw := tabwriter.NewWriter(&buf, 0, 0, 1, ' ', tabwriter.Debug) + + fmt.Fprint(tw, "NAME\tCREATED AT\tSTATUS\tTYPE\tPROVIDER\n") for _, cluster := range clusters { - content = content + fmt.Sprintf("|%s|%s|%s|%s|%s\n", + fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\n", cluster.ClusterName, cluster.CreationTimestamp, cluster.Status, @@ -31,5 +32,5 @@ func displayFormattedClusterInfo(clusters []types.Cluster) { ) } - progress.Success(header + content) + progress.Success(buf.String()) } diff --git a/internal/progress/command.go b/internal/progress/command.go index a0f92710..7d69d355 100644 --- a/internal/progress/command.go +++ b/internal/progress/command.go @@ -7,6 +7,7 @@ See the LICENSE file for more details. package progress import ( + "log" "time" tea "github.com/charmbracelet/bubbletea" @@ -19,6 +20,8 @@ func GetClusterInterval(clusterName string) tea.Cmd { return tea.Every(time.Second*10, func(t time.Time) tea.Msg { provisioningCluster, err := cluster.GetCluster(clusterName) if err != nil { + log.Printf("failed to get cluster %q: %v", clusterName, err) + return nil } return CusterProvisioningMsg(provisioningCluster) diff --git a/internal/progress/constants.go b/internal/progress/constants.go index 8e2ba944..41335b83 100644 --- a/internal/progress/constants.go +++ b/internal/progress/constants.go @@ -33,13 +33,13 @@ var CompletedStepsLabels = ProvisionSteps{ domain_liveness_check: "Domain liveness check", kbot_setup_check: "Kbot setup", git_init_check: "Initializing Git", - gitops_ready_check: "Initializing gitops", + gitops_ready_check: "Initializing GitOps", git_terraform_apply_check: "Git Terraform apply", - gitops_pushed_check: "Gitops repos pushed", + gitops_pushed_check: "GitOps repos pushed", cloud_terraform_apply_check: "Cloud Terraform apply", cluster_secrets_created_check: "Creating cluster secrets", - argocd_install_check: "Installing Argo CD", - argocd_initialize_check: "Initializing Argo CD", + argocd_install_check: "Installing ArgoCD", + argocd_initialize_check: "Initializing ArgoCD", vault_initialized_check: "Initializing Vault", vault_terraform_apply_check: "Vault Terraform apply", users_terraform_apply_check: "Users Terraform apply", diff --git a/internal/progress/message.go b/internal/progress/message.go index 901af7f9..daceb3aa 100644 --- a/internal/progress/message.go +++ b/internal/progress/message.go @@ -56,7 +56,7 @@ func DisplayLogHints(estimatedTime int) { documentationLink := "https://docs.kubefirst.io/" if cloudProvider != "" { - documentationLink = documentationLink + cloudProvider + `/quick-start/install/cli` + documentationLink += cloudProvider + `/quick-start/install/cli` } header := ` @@ -80,40 +80,26 @@ func DisplaySuccessMessage(cluster types.Cluster) successMsg { cloudCliKubeconfig := "" gitProviderLabel := "GitHub" - if cluster.GitProvider == "gitlab" { gitProviderLabel = "GitLab" } switch cluster.CloudProvider { case "aws": - cloudCliKubeconfig = fmt.Sprintf("aws eks update-kubeconfig --name %s --region %s", cluster.ClusterName, cluster.CloudRegion) - break - + cloudCliKubeconfig = fmt.Sprintf("aws eks update-kubeconfig --name %q --region %q", cluster.ClusterName, cluster.CloudRegion) case "civo": - cloudCliKubeconfig = fmt.Sprintf("civo kubernetes config %s --save", cluster.ClusterName) - break - + cloudCliKubeconfig = fmt.Sprintf("civo kubernetes config %q --save", cluster.ClusterName) case "digitalocean": cloudCliKubeconfig = "doctl kubernetes cluster kubeconfig save " + cluster.ClusterName - break - case "google": - cloudCliKubeconfig = fmt.Sprintf("gcloud container clusters get-credentials %s --region=%s", cluster.ClusterName, cluster.CloudRegion) - break - + cloudCliKubeconfig = fmt.Sprintf("gcloud container clusters get-credentials %q --region=%q", cluster.ClusterName, cluster.CloudRegion) case "vultr": - cloudCliKubeconfig = fmt.Sprintf("vultr-cli kubernetes config %s", cluster.ClusterName) - break - + cloudCliKubeconfig = fmt.Sprintf("vultr-cli kubernetes config %q", cluster.ClusterName) case "k3s": - cloudCliKubeconfig = fmt.Sprint(("use the kubeconfig file outputed from terraform to acces to the cluster")) - break - + cloudCliKubeconfig = "use the kubeconfig file outputted from terraform to access the cluster" } var fullDomainName string - if cluster.SubdomainName != "" { fullDomainName = fmt.Sprintf("%s.%s", cluster.SubdomainName, cluster.DomainName) } else { @@ -124,7 +110,7 @@ func DisplaySuccessMessage(cluster types.Cluster) successMsg { ## #### :tada: Success` + "`Cluster " + cluster.ClusterName + " is now up and running`" + ` -# Cluster ` + cluster.ClusterName + `” details: +# Cluster ` + cluster.ClusterName + ` details: ### :bulb: To retrieve root credentials for your Kubefirst platform run: ##### kubefirst ` + cluster.CloudProvider + ` root-credentials @@ -137,7 +123,7 @@ func DisplaySuccessMessage(cluster types.Cluster) successMsg { ### URL ` + fmt.Sprintf("`https://kubefirst.%s`", fullDomainName) + ` ## Argo CD ### URL ` + fmt.Sprintf("`https://argocd.%s`", fullDomainName) + ` -## Vault +## Vault ### URL ` + fmt.Sprintf("`https://vault.%s`", fullDomainName) + ` @@ -149,6 +135,7 @@ func DisplaySuccessMessage(cluster types.Cluster) successMsg { ### To view all cluster pods run: ##### kubectl get pods -A ` + successMessage := renderMessage(success) return successMsg{ diff --git a/internal/progress/progress.go b/internal/progress/progress.go index adcd2d97..b33d9ba7 100644 --- a/internal/progress/progress.go +++ b/internal/progress/progress.go @@ -75,7 +75,6 @@ func (m progressModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.provisioningCluster.Status == "error" { errorMessage := createErrorLog(m.provisioningCluster.LastCondition) m.error = errorMessage.message - return m, tea.Quit } diff --git a/internal/progress/styles.go b/internal/progress/styles.go index e1202e8f..d9971d64 100644 --- a/internal/progress/styles.go +++ b/internal/progress/styles.go @@ -1,6 +1,8 @@ package progress -import "github.com/charmbracelet/glamour/ansi" +import ( + "github.com/charmbracelet/glamour/ansi" +) var StyleConfig = ansi.StyleConfig{ Document: ansi.StyleBlock{ diff --git a/internal/progress/types.go b/internal/progress/types.go index a196a637..ac276fbe 100644 --- a/internal/progress/types.go +++ b/internal/progress/types.go @@ -26,7 +26,7 @@ type progressModel struct { successMessage string } -// Bubbletea messsages +// Bubbletea messages type CusterProvisioningMsg types.Cluster diff --git a/internal/provision/provision.go b/internal/provision/provision.go index 28db15cc..a97b86e3 100644 --- a/internal/provision/provision.go +++ b/internal/provision/provision.go @@ -7,6 +7,8 @@ See the LICENSE file for more details. package provision import ( + "fmt" + apiTypes "github.com/konstructio/kubefirst-api/pkg/types" "github.com/konstructio/kubefirst/internal/cluster" "github.com/konstructio/kubefirst/internal/progress" @@ -15,7 +17,7 @@ import ( "github.com/rs/zerolog/log" ) -func CreateMgmtCluster(gitAuth apiTypes.GitAuth, cliFlags types.CliFlags, catalogApps []apiTypes.GitopsCatalogApp) { +func CreateMgmtCluster(gitAuth apiTypes.GitAuth, cliFlags types.CliFlags, catalogApps []apiTypes.GitopsCatalogApp) error { clusterRecord := utilities.CreateClusterDefinitionRecordFromRaw( gitAuth, cliFlags, @@ -24,20 +26,26 @@ func CreateMgmtCluster(gitAuth apiTypes.GitAuth, cliFlags types.CliFlags, catalo clusterCreated, err := cluster.GetCluster(clusterRecord.ClusterName) if err != nil { - log.Info().Msg("cluster not found") + log.Printf("error retrieving cluster %q: %v", clusterRecord.ClusterName, err) + return fmt.Errorf("error retrieving cluster: %w", err) } if !clusterCreated.InProgress { err = cluster.CreateCluster(clusterRecord) if err != nil { progress.Error(err.Error()) + return fmt.Errorf("error creating cluster: %w", err) } } if clusterCreated.Status == "error" { cluster.ResetClusterProgress(clusterRecord.ClusterName) - cluster.CreateCluster(clusterRecord) + if err := cluster.CreateCluster(clusterRecord); err != nil { + progress.Error(err.Error()) + return fmt.Errorf("error re-creating cluster after error state: %w", err) + } } progress.StartProvisioning(clusterRecord.ClusterName) + return nil } diff --git a/internal/provisionLogs/command.go b/internal/provisionLogs/command.go index 0d089f62..e8b2c739 100644 --- a/internal/provisionLogs/command.go +++ b/internal/provisionLogs/command.go @@ -39,11 +39,10 @@ func AddLog(logMsg string) { } else { parsedTime, err := time.Parse(time.RFC3339, log.Time) if err != nil { - fmt.Println("Error parsing date:", err) + fmt.Printf("error parsing date: %v\n", err) return } - // Format the parsed time into the desired format formattedDateStr := parsedTime.Format("2006-01-02 15:04:05") timeLog := timeStyle(formattedDateStr) diff --git a/internal/provisionLogs/message.go b/internal/provisionLogs/message.go index bfaf9d06..1b5ec504 100644 --- a/internal/provisionLogs/message.go +++ b/internal/provisionLogs/message.go @@ -10,6 +10,7 @@ Color definition https://www.ditig.com/256-colors-cheat-sheet package provisionLogs import ( + "fmt" "log" "github.com/charmbracelet/glamour" @@ -24,8 +25,9 @@ func renderMessage(message string) string { out, err := r.Render(message) if err != nil { - log.Println(err.Error()) - return err.Error() + s := fmt.Errorf("rendering message failed: %w", err) + log.Println(s) + return s.Error() } return out } diff --git a/internal/provisionLogs/provisionLogs.go b/internal/provisionLogs/provisionLogs.go index c3648b2b..4b63fb7e 100644 --- a/internal/provisionLogs/provisionLogs.go +++ b/internal/provisionLogs/provisionLogs.go @@ -50,7 +50,7 @@ func (m provisionLogsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m provisionLogsModel) View() string { logs := "" for i := 0; i < len(m.logs); i++ { - logs = logs + m.logs[i] + "\n" + logs += m.logs[i] + "\n" } return logs + "\n" + quitStyle("ctrl+c to quit") + "\n" diff --git a/internal/provisionLogs/types.go b/internal/provisionLogs/types.go index e8a69e13..a67905f2 100644 --- a/internal/provisionLogs/types.go +++ b/internal/provisionLogs/types.go @@ -11,7 +11,7 @@ type provisionLogsModel struct { logs []string } -// Bubbletea messsages +// Bubbletea messages type logMessage struct { message string } diff --git a/internal/segment/segment.go b/internal/segment/segment.go index c2f2120f..927b7c3e 100644 --- a/internal/segment/segment.go +++ b/internal/segment/segment.go @@ -1,6 +1,7 @@ package segment import ( + "fmt" "os" "github.com/denisbrodbeck/machineid" @@ -13,8 +14,11 @@ const ( kubefirstClient string = "api" ) -func InitClient(clusterId, clusterType, gitProvider string) telemetry.TelemetryEvent { - machineID, _ := machineid.ID() +func InitClient(clusterId, clusterType, gitProvider string) (telemetry.TelemetryEvent, error) { + machineID, err := machineid.ID() + if err != nil { + return telemetry.TelemetryEvent{}, fmt.Errorf("failed to get machine ID: %w", err) + } c := telemetry.TelemetryEvent{ CliVersion: configs.K1Version, @@ -33,5 +37,5 @@ func InitClient(clusterId, clusterType, gitProvider string) telemetry.TelemetryE UserId: machineID, } - return c + return c, nil } diff --git a/internal/utilities/flags.go b/internal/utilities/flags.go index 13daceaa..ed0ff19c 100644 --- a/internal/utilities/flags.go +++ b/internal/utilities/flags.go @@ -7,126 +7,127 @@ See the LICENSE file for more details. package utilities import ( + "fmt" "strings" "github.com/konstructio/kubefirst/internal/progress" "github.com/konstructio/kubefirst/internal/types" - "github.com/spf13/cobra" "github.com/spf13/viper" ) func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) { cliFlags := types.CliFlags{} + alertsEmailFlag, err := cmd.Flags().GetString("alerts-email") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get alerts-email flag: %w", err) } cloudRegionFlag, err := cmd.Flags().GetString("cloud-region") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get cloud-region flag: %w", err) } clusterNameFlag, err := cmd.Flags().GetString("cluster-name") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get cluster-name flag: %w", err) } dnsProviderFlag, err := cmd.Flags().GetString("dns-provider") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get dns-provider flag: %w", err) } subdomainFlag, err := cmd.Flags().GetString("subdomain") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get subdomain flag: %w", err) } domainNameFlag, err := cmd.Flags().GetString("domain-name") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get domain-name flag: %w", err) } githubOrgFlag, err := cmd.Flags().GetString("github-org") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get github-org flag: %w", err) } githubOrgFlag = strings.ToLower(githubOrgFlag) gitlabGroupFlag, err := cmd.Flags().GetString("gitlab-group") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get gitlab-group flag: %w", err) } gitlabGroupFlag = strings.ToLower(gitlabGroupFlag) gitProviderFlag, err := cmd.Flags().GetString("git-provider") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get git-provider flag: %w", err) } gitProtocolFlag, err := cmd.Flags().GetString("git-protocol") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get git-protocol flag: %w", err) } gitopsTemplateURLFlag, err := cmd.Flags().GetString("gitops-template-url") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get gitops-template-url flag: %w", err) } gitopsTemplateBranchFlag, err := cmd.Flags().GetString("gitops-template-branch") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get gitops-template-branch flag: %w", err) } useTelemetryFlag, err := cmd.Flags().GetBool("use-telemetry") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get use-telemetry flag: %w", err) } nodeTypeFlag, err := cmd.Flags().GetString("node-type") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get node-type flag: %w", err) } installCatalogAppsFlag, err := cmd.Flags().GetString("install-catalog-apps") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get install-catalog-apps flag: %w", err) } nodeCountFlag, err := cmd.Flags().GetString("node-count") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get node-count flag: %w", err) } installKubefirstProFlag, err := cmd.Flags().GetBool("install-kubefirst-pro") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get install-kubefirst-pro flag: %w", err) } if cloudProvider == "aws" { ecrFlag, err := cmd.Flags().GetBool("ecr") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get ecr flag: %w", err) } cliFlags.Ecr = ecrFlag @@ -136,46 +137,45 @@ func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) googleProject, err := cmd.Flags().GetString("google-project") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get google-project flag: %w", err) } cliFlags.GoogleProject = googleProject } - // TODO: reafactor this part if cloudProvider == "k3s" { k3sServersPrivateIps, err := cmd.Flags().GetStringSlice("servers-private-ips") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get servers-private-ips flag: %w", err) } cliFlags.K3sServersPrivateIps = k3sServersPrivateIps k3sServersPublicIps, err := cmd.Flags().GetStringSlice("servers-public-ips") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get servers-public-ips flag: %w", err) } cliFlags.K3sServersPublicIps = k3sServersPublicIps k3sSshUserFlag, err := cmd.Flags().GetString("ssh-user") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get ssh-user flag: %w", err) } cliFlags.K3sSshUser = k3sSshUserFlag k3sSshPrivateKeyFlag, err := cmd.Flags().GetString("ssh-privatekey") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get ssh-privatekey flag: %w", err) } cliFlags.K3sSshPrivateKey = k3sSshPrivateKeyFlag K3sServersArgsFlags, err := cmd.Flags().GetStringSlice("servers-args") if err != nil { progress.Error(err.Error()) - return cliFlags, err + return cliFlags, fmt.Errorf("failed to get servers-args flag: %w", err) } cliFlags.K3sServersArgs = K3sServersArgsFlags } @@ -214,7 +214,9 @@ func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) viper.Set("flags.ssh-privatekey", cliFlags.K3sSshPrivateKey) viper.Set("flags.servers-args", cliFlags.K3sServersArgs) } - viper.WriteConfig() + if err := viper.WriteConfig(); err != nil { + return cliFlags, fmt.Errorf("failed to write configuration: %w", err) + } return cliFlags, nil } diff --git a/internal/utilities/utilities.go b/internal/utilities/utilities.go index 41c626fb..9b9b759d 100644 --- a/internal/utilities/utilities.go +++ b/internal/utilities/utilities.go @@ -10,7 +10,7 @@ import ( "bufio" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "os" "strconv" @@ -34,12 +34,14 @@ func CreateK1ClusterDirectory(clusterName string) { homePath, err := os.UserHomeDir() if err != nil { log.Info().Msg(err.Error()) + return } + k1Dir := fmt.Sprintf("%s/.k1/%s", homePath, clusterName) if _, err := os.Stat(k1Dir); os.IsNotExist(err) { err := os.MkdirAll(k1Dir, os.ModePerm) if err != nil { - log.Info().Msgf("%s directory already exists, continuing", k1Dir) + log.Info().Msgf("%q directory already exists, continuing", k1Dir) } } } @@ -104,7 +106,6 @@ func CreateClusterRecordFromRaw( case "civo": cl.CivoAuth.Token = os.Getenv("CIVO_TOKEN") case "aws": - // ToDo: where to get credentials? cl.AWSAuth.AccessKeyID = viper.GetString("kubefirst.state-store-creds.access-key-id") cl.AWSAuth.SecretAccessKey = viper.GetString("kubefirst.state-store-creds.secret-access-key-id") cl.AWSAuth.SessionToken = viper.GetString("kubefirst.state-store-creds.token") @@ -188,7 +189,6 @@ func CreateClusterDefinitionRecordFromRaw(gitAuth apiTypes.GitAuth, cliFlags typ case "akamai": cl.AkamaiAuth.Token = os.Getenv("LINODE_TOKEN") case "aws": - // ToDo: where to get credentials? cl.AWSAuth.AccessKeyID = viper.GetString("kubefirst.state-store-creds.access-key-id") cl.AWSAuth.SecretAccessKey = viper.GetString("kubefirst.state-store-creds.secret-access-key-id") cl.AWSAuth.SessionToken = viper.GetString("kubefirst.state-store-creds.token") @@ -212,11 +212,16 @@ func CreateClusterDefinitionRecordFromRaw(gitAuth apiTypes.GitAuth, cliFlags typ jsonFile, err := os.Open(jsonFilePath) if err != nil { - progress.Error("Unable to read GOOGLE_APPLICATION_CREDENTIALS file") + progress.Error(fmt.Sprintf("unable to read GOOGLE_APPLICATION_CREDENTIALS file: %s", err)) + return apiTypes.ClusterDefinition{} } defer jsonFile.Close() - jsonContent, _ := ioutil.ReadAll(jsonFile) + jsonContent, err := io.ReadAll(jsonFile) + if err != nil { + progress.Error(fmt.Sprintf("unable to read GOOGLE_APPLICATION_CREDENTIALS file content: %s", err)) + return apiTypes.ClusterDefinition{} + } cl.GoogleAuth.KeyFile = string(jsonContent) cl.GoogleAuth.ProjectId = cliFlags.GoogleProject @@ -237,11 +242,13 @@ func ExportCluster(cluster apiTypes.Cluster, kcfg *k8s.KubernetesClient) error { bytes, err := json.Marshal(cluster) if err != nil { - log.Error().Msg(err.Error()) - return err + return fmt.Errorf("error marshaling cluster: %w", err) } - secretValuesMap, _ := ParseJSONToMap(string(bytes)) + secretValuesMap, err := ParseJSONToMap(string(bytes)) + if err != nil { + return fmt.Errorf("error parsing JSON to map: %w", err) + } secret := &v1secret.Secret{ ObjectMeta: metav1.ObjectMeta{Name: "kubefirst-initial-state", Namespace: "kubefirst"}, @@ -250,11 +257,13 @@ func ExportCluster(cluster apiTypes.Cluster, kcfg *k8s.KubernetesClient) error { err = k8s.CreateSecretV2(kcfg.Clientset, secret) if err != nil { - return fmt.Errorf(fmt.Sprintf("unable to save secret to management cluster. %s", err)) + return fmt.Errorf("unable to save secret to management cluster: %w", err) } viper.Set("kubefirst-checks.secret-export-state", true) - viper.WriteConfig() + if err := viper.WriteConfig(); err != nil { + return fmt.Errorf("error writing config: %w", err) + } return nil } @@ -264,18 +273,19 @@ func ConsumeStream(url string) { req, err := http.NewRequest("GET", url, nil) if err != nil { - fmt.Println("Error creating request:", err) + log.Error().Msgf("Error creating request: %s", err) return } resp, err := client.Do(req) if err != nil { - fmt.Println("Error making request:", err) + log.Error().Msgf("Error making request: %s", err) return } + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - fmt.Println("Error response:", resp.Status) + log.Error().Msgf("Unexpected status code: %s", resp.Status) return } @@ -287,16 +297,16 @@ func ConsumeStream(url string) { } if err := scanner.Err(); err != nil { - log.Error().Msgf("Error reading response: %s", err.Error()) + log.Error().Msgf("Error reading response: %s", err) return } } func ParseJSONToMap(jsonStr string) (map[string][]byte, error) { var result map[string]interface{} - err := json.Unmarshal([]byte(jsonStr), &result) - if err != nil { - return nil, err + + if err := json.Unmarshal([]byte(jsonStr), &result); err != nil { + return nil, fmt.Errorf("error unmarshaling JSON: %w", err) } secretData := make(map[string][]byte) @@ -305,13 +315,13 @@ func ParseJSONToMap(jsonStr string) (map[string][]byte, error) { case map[string]interface{}, []interface{}: // For nested structures, marshal back to JSON bytes, err := json.Marshal(v) if err != nil { - return nil, err + return nil, fmt.Errorf("error marshaling value for key %q: %w", key, err) } secretData[key] = bytes default: bytes, err := json.Marshal(v) if err != nil { - return nil, err + return nil, fmt.Errorf("error marshaling value for key %q: %w", key, err) } secretData[key] = bytes } From 9d52cd4c26b4c3ba642c813eb8d774714764da25 Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:06:51 -0400 Subject: [PATCH 5/9] Fix compilation --- cmd/k3d/create.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/k3d/create.go b/cmd/k3d/create.go index 5eb6df74..1d55f596 100644 --- a/cmd/k3d/create.go +++ b/cmd/k3d/create.go @@ -278,7 +278,10 @@ func runK3d(cmd *cobra.Command, args []string) error { viper.WriteConfig() } - segClient := segment.InitClient(clusterId, clusterTypeFlag, gitProviderFlag) + segClient, err := segment.InitClient(clusterId, clusterTypeFlag, gitProviderFlag) + if err != nil { + return fmt.Errorf("failed to initialize segment client: %w", err) + } progressPrinter.AddTracker("preflight-checks", "Running preflight checks", 5) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), false) From 3825c3ea5617ac1e67eccda8c70624e5e022ecd8 Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:39:03 -0400 Subject: [PATCH 6/9] Update using final standard linters --- cmd/akamai/command.go | 2 +- cmd/akamai/create.go | 4 +- cmd/aws/command.go | 2 +- cmd/aws/create.go | 4 +- cmd/beta.go | 2 +- cmd/civo/backup.go | 2 +- cmd/civo/command.go | 2 +- cmd/civo/create.go | 4 +- cmd/civo/quota.go | 2 +- cmd/digitalocean/command.go | 2 +- cmd/digitalocean/create.go | 4 +- cmd/google/command.go | 2 +- cmd/google/create.go | 6 +- cmd/info.go | 2 +- cmd/k3d/command.go | 2 +- cmd/k3d/create.go | 87 ++++++++++++----------- cmd/k3d/vault.go | 2 +- cmd/k3s/command.go | 8 +-- cmd/k3s/create.go | 6 +- cmd/launch.go | 8 +-- cmd/letsencrypt.go | 2 +- cmd/logs.go | 2 +- cmd/reset.go | 2 +- cmd/root.go | 2 +- cmd/terraform.go | 2 +- cmd/version.go | 2 +- cmd/vultr/command.go | 2 +- cmd/vultr/create.go | 5 +- go.mod | 2 +- internal/cluster/cluster.go | 17 ++--- internal/common/common.go | 6 +- internal/gitShim/containerRegistryAuth.go | 3 +- internal/gitShim/init.go | 2 +- internal/helm/types.go | 4 +- internal/k3d/menu.go | 8 +-- internal/launch/cmd.go | 14 ++-- internal/progress/command.go | 62 ++++++++-------- internal/progress/constants.go | 50 ++++--------- internal/progress/message.go | 1 + internal/progress/progress.go | 5 +- internal/progress/types.go | 28 ++++---- internal/provisionLogs/command.go | 2 +- internal/provisionLogs/message.go | 33 --------- internal/provisionLogs/provisionLogs.go | 3 +- internal/provisionLogs/types.go | 2 +- internal/segment/segment.go | 4 +- internal/types/flags.go | 12 ++-- internal/types/proxy.go | 4 +- internal/utilities/flags.go | 26 +++---- internal/utilities/utilities.go | 6 +- main.go | 46 ++++++------ 51 files changed, 232 insertions(+), 280 deletions(-) delete mode 100644 internal/provisionLogs/message.go diff --git a/cmd/akamai/command.go b/cmd/akamai/command.go index 167a8619..f3e4566e 100644 --- a/cmd/akamai/command.go +++ b/cmd/akamai/command.go @@ -54,7 +54,7 @@ func NewCommand() *cobra.Command { Use: "akamai", Short: "kubefirst akamai installation", Long: "kubefirst akamai", - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about akamai in kubefirst, run:") fmt.Println(" kubefirst beta akamai --help") diff --git a/cmd/akamai/create.go b/cmd/akamai/create.go index b4e23a51..9179beec 100644 --- a/cmd/akamai/create.go +++ b/cmd/akamai/create.go @@ -25,7 +25,7 @@ import ( "github.com/spf13/viper" ) -func createAkamai(cmd *cobra.Command, args []string) error { +func createAkamai(cmd *cobra.Command, _ []string) error { cliFlags, err := utilities.GetFlags(cmd, "akamai") if err != nil { progress.Error(err.Error()) @@ -82,7 +82,7 @@ func createAkamai(cmd *cobra.Command, args []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") + err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("kubefirst api is unavailable: %w", err) diff --git a/cmd/aws/command.go b/cmd/aws/command.go index bdf80129..f011c82a 100644 --- a/cmd/aws/command.go +++ b/cmd/aws/command.go @@ -49,7 +49,7 @@ func NewCommand() *cobra.Command { Use: "aws", Short: "kubefirst aws installation", Long: "kubefirst aws", - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about aws in kubefirst, run:") fmt.Println(" kubefirst help") diff --git a/cmd/aws/create.go b/cmd/aws/create.go index 4c9a00ef..a19203af 100644 --- a/cmd/aws/create.go +++ b/cmd/aws/create.go @@ -27,7 +27,7 @@ import ( "github.com/spf13/viper" ) -func createAws(cmd *cobra.Command, args []string) error { +func createAws(cmd *cobra.Command, _ []string) error { cliFlags, err := utilities.GetFlags(cmd, "aws") if err != nil { progress.Error(err.Error()) @@ -118,7 +118,7 @@ func createAws(cmd *cobra.Command, args []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") + err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("failed to check kubefirst API availability: %w", err) diff --git a/cmd/beta.go b/cmd/beta.go index 395311da..9afd9155 100644 --- a/cmd/beta.go +++ b/cmd/beta.go @@ -22,7 +22,7 @@ var betaCmd = &cobra.Command{ Use: "beta", Short: "access Kubefirst beta features", Long: `access Kubefirst beta features`, - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about Kubefirst, run:") fmt.Println(" kubefirst help") diff --git a/cmd/civo/backup.go b/cmd/civo/backup.go index 74d37e68..c6694f65 100644 --- a/cmd/civo/backup.go +++ b/cmd/civo/backup.go @@ -18,7 +18,7 @@ import ( "github.com/spf13/viper" ) -func backupCivoSSL(cmd *cobra.Command, _ []string) error { +func backupCivoSSL(_ *cobra.Command, _ []string) error { utils.DisplayLogHints() clusterName := viper.GetString("flags.cluster-name") diff --git a/cmd/civo/command.go b/cmd/civo/command.go index 1c09088a..2ea8bbc7 100644 --- a/cmd/civo/command.go +++ b/cmd/civo/command.go @@ -54,7 +54,7 @@ func NewCommand() *cobra.Command { Use: "civo", Short: "Kubefirst Civo installation", Long: "Kubefirst Civo", - Run: func(cmd *cobra.Command, _ []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about Civo in Kubefirst, run:") fmt.Println(" kubefirst civo --help") diff --git a/cmd/civo/create.go b/cmd/civo/create.go index d59699ee..d6ad36d9 100644 --- a/cmd/civo/create.go +++ b/cmd/civo/create.go @@ -25,7 +25,7 @@ import ( "github.com/spf13/viper" ) -func createCivo(cmd *cobra.Command, args []string) error { +func createCivo(cmd *cobra.Command, _ []string) error { cliFlags, err := utilities.GetFlags(cmd, "civo") if err != nil { progress.Error(err.Error()) @@ -87,7 +87,7 @@ func createCivo(cmd *cobra.Command, args []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("API availability check failed: %w", err) diff --git a/cmd/civo/quota.go b/cmd/civo/quota.go index a5d129d6..e171f2fe 100644 --- a/cmd/civo/quota.go +++ b/cmd/civo/quota.go @@ -165,7 +165,7 @@ func printCivoQuotaWarning(messageHeader string, output []string) string { } // evalCivoQuota provides an interface to the command-line -func evalCivoQuota(cmd *cobra.Command, args []string) error { +func evalCivoQuota(cmd *cobra.Command, _ []string) error { civoToken := os.Getenv("CIVO_TOKEN") if len(civoToken) == 0 { return fmt.Errorf("your CIVO_TOKEN environment variable isn't set, visit this link https://dashboard.civo.com/security and set CIVO_TOKEN") diff --git a/cmd/digitalocean/command.go b/cmd/digitalocean/command.go index 3ab8a3f0..2ca962a0 100644 --- a/cmd/digitalocean/command.go +++ b/cmd/digitalocean/command.go @@ -54,7 +54,7 @@ func NewCommand() *cobra.Command { Use: "digitalocean", Short: "Kubefirst DigitalOcean installation", Long: "Kubefirst DigitalOcean", - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about DigitalOcean in Kubefirst, run:") fmt.Println(" kubefirst digitalocean --help") diff --git a/cmd/digitalocean/create.go b/cmd/digitalocean/create.go index 3c705b0f..a600c619 100644 --- a/cmd/digitalocean/create.go +++ b/cmd/digitalocean/create.go @@ -26,7 +26,7 @@ import ( "github.com/spf13/viper" ) -func createDigitalocean(cmd *cobra.Command, args []string) error { +func createDigitalocean(cmd *cobra.Command, _ []string) error { cliFlags, err := utilities.GetFlags(cmd, "digitalocean") if err != nil { progress.Error(err.Error()) @@ -96,7 +96,7 @@ func createDigitalocean(cmd *cobra.Command, args []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("failed to check app availability for Kubefirst API: %w", err) diff --git a/cmd/google/command.go b/cmd/google/command.go index 009e0e30..bed2ddc2 100644 --- a/cmd/google/command.go +++ b/cmd/google/command.go @@ -57,7 +57,7 @@ func NewCommand() *cobra.Command { Use: "google", Short: "kubefirst Google installation", Long: "kubefirst google", - Run: func(cmd *cobra.Command, _ []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about google in kubefirst, run:") fmt.Println(" kubefirst beta google --help") diff --git a/cmd/google/create.go b/cmd/google/create.go index 087eb928..00fd53dc 100644 --- a/cmd/google/create.go +++ b/cmd/google/create.go @@ -23,10 +23,10 @@ import ( "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" - _ "k8s.io/client-go/plugin/pkg/client/auth" + _ "k8s.io/client-go/plugin/pkg/client/auth" // required for authentication ) -func createGoogle(cmd *cobra.Command, args []string) error { +func createGoogle(cmd *cobra.Command, _ []string) error { cliFlags, err := utilities.GetFlags(cmd, "google") if err != nil { progress.Error(err.Error()) @@ -89,7 +89,7 @@ func createGoogle(cmd *cobra.Command, args []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("kubefirst api availability check failed: %w", err) diff --git a/cmd/info.go b/cmd/info.go index 5b6cc2e4..8581cd3f 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -22,7 +22,7 @@ var infoCmd = &cobra.Command{ Use: "info", Short: "provides general Kubefirst setup data", Long: `Provides machine data, files and folders paths`, - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { config := configs.ReadConfig() var buf bytes.Buffer diff --git a/cmd/k3d/command.go b/cmd/k3d/command.go index 13bc7422..5417f767 100644 --- a/cmd/k3d/command.go +++ b/cmd/k3d/command.go @@ -48,7 +48,7 @@ func NewCommand() *cobra.Command { Use: "k3d", Short: "kubefirst k3d installation", Long: "kubefirst k3d", - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about k3d in kubefirst, run:") fmt.Println(" kubefirst k3d --help") diff --git a/cmd/k3d/create.go b/cmd/k3d/create.go index 1d55f596..f70fefb4 100644 --- a/cmd/k3d/create.go +++ b/cmd/k3d/create.go @@ -56,7 +56,8 @@ import ( "k8s.io/apimachinery/pkg/util/wait" ) -func runK3d(cmd *cobra.Command, args []string) error { +//nolint:gocyclo // this function is complex and needs to be refactored +func runK3d(cmd *cobra.Command, _ []string) error { ciFlag, err := cmd.Flags().GetBool("ci") if err != nil { return fmt.Errorf("failed to get 'ci' flag: %w", err) @@ -121,8 +122,12 @@ func runK3d(cmd *cobra.Command, args []string) error { utils.DisplayLogHints() isValid, catalogApps, err := catalog.ValidateCatalogApps(installCatalogAppsFlag) + if err != nil { + return fmt.Errorf("failed to validate catalog apps: %w", err) + } + if !isValid { - return err + return errors.New("catalog apps validation failed") } switch gitProviderFlag { @@ -271,14 +276,14 @@ func runK3d(cmd *cobra.Command, args []string) error { var sshPrivateKey, sshPublicKey string // todo placed in configmap in kubefirst namespace, included in telemetry - clusterId := viper.GetString("kubefirst.cluster-id") - if clusterId == "" { - clusterId = utils.GenerateClusterID() - viper.Set("kubefirst.cluster-id", clusterId) + clusterID := viper.GetString("kubefirst.cluster-id") + if clusterID == "" { + clusterID = utils.GenerateClusterID() + viper.Set("kubefirst.cluster-id", clusterID) viper.WriteConfig() } - segClient, err := segment.InitClient(clusterId, clusterTypeFlag, gitProviderFlag) + segClient, err := segment.InitClient(clusterID, clusterTypeFlag, gitProviderFlag) if err != nil { return fmt.Errorf("failed to initialize segment client: %w", err) } @@ -438,7 +443,7 @@ func runK3d(cmd *cobra.Command, args []string) error { KubeconfigPath: config.Kubeconfig, GitopsRepoURL: gitopsRepoURL, GitProvider: config.GitProvider, - ClusterId: clusterId, + ClusterId: clusterID, CloudProvider: k3d.CloudProvider, } @@ -642,7 +647,7 @@ func runK3d(cmd *cobra.Command, args []string) error { msg := fmt.Errorf("error pushing detokenized gitops repository to remote %q: %w", config.DestinationGitopsRepoGitURL, err) telemetry.SendEvent(segClient, telemetry.GitopsRepoPushFailed, msg.Error()) if !strings.Contains(msg.Error(), "already up-to-date") { - log.Printf(msg.Error()) + log.Print(msg.Error()) return msg } } @@ -867,12 +872,12 @@ func runK3d(cmd *cobra.Command, args []string) error { ) + ":8080" argoCDToken, err = argocd.GetArgocdTokenV2(httpClient, argoCDHTTPURL, "admin", argocdPassword) if err != nil { - return err + return fmt.Errorf("failed to get ArgoCD token: %w", err) } } else { argoCDToken, err = argocd.GetArgocdTokenV2(httpClient, k3d.ArgocdURL, "admin", argocdPassword) if err != nil { - return err + return fmt.Errorf("failed to get ArgoCD token: %w", err) } } @@ -1228,41 +1233,41 @@ func runK3d(cmd *cobra.Command, args []string) error { viper.Set("kubefirst-checks.cluster-install-complete", false) viper.WriteConfig() return fmt.Errorf("failed to export cluster object: %w", err) - } else { - kubefirstDeployment, err := k8s.ReturnDeploymentObject( - kcfg.Clientset, - "app.kubernetes.io/instance", - "kubefirst", - "kubefirst", - 600, - ) - if err != nil { - return fmt.Errorf("error finding kubefirst Deployment: %w", err) - } - _, err = k8s.WaitForDeploymentReady(kcfg.Clientset, kubefirstDeployment, 120) - if err != nil { - return fmt.Errorf("error waiting for kubefirst Deployment ready state: %w", err) - } - progressPrinter.IncrementTracker("wrapping-up", 1) + } - err = utils.OpenBrowser(constants.KubefirstConsoleLocalURLTLS) - if err != nil { - log.Error().Err(err).Msg("failed to open Kubefirst console in browser") - } + kubefirstDeployment, err := k8s.ReturnDeploymentObject( + kcfg.Clientset, + "app.kubernetes.io/instance", + "kubefirst", + "kubefirst", + 600, + ) + if err != nil { + return fmt.Errorf("error finding kubefirst Deployment: %w", err) + } + _, err = k8s.WaitForDeploymentReady(kcfg.Clientset, kubefirstDeployment, 120) + if err != nil { + return fmt.Errorf("error waiting for kubefirst Deployment ready state: %w", err) + } + progressPrinter.IncrementTracker("wrapping-up", 1) - telemetry.SendEvent(segClient, telemetry.ClusterInstallCompleted, "") - viper.Set("kubefirst-checks.cluster-install-complete", true) - viper.WriteConfig() + err = utils.OpenBrowser(constants.KubefirstConsoleLocalURLTLS) + if err != nil { + log.Error().Err(err).Msg("failed to open Kubefirst console in browser") + } - log.Info().Msg("kubefirst installation complete") - log.Info().Msg("welcome to your new Kubefirst platform running in K3D") - time.Sleep(1 * time.Second) + telemetry.SendEvent(segClient, telemetry.ClusterInstallCompleted, "") + viper.Set("kubefirst-checks.cluster-install-complete", true) + viper.WriteConfig() - reports.LocalHandoffScreenV2(viper.GetString("components.argocd.password"), clusterNameFlag, gitDestDescriptor, cGitOwner, config, ciFlag) + log.Info().Msg("kubefirst installation complete") + log.Info().Msg("welcome to your new Kubefirst platform running in K3D") + time.Sleep(1 * time.Second) - if ciFlag { - progress.Progress.Quit() - } + reports.LocalHandoffScreenV2(viper.GetString("components.argocd.password"), clusterNameFlag, gitDestDescriptor, cGitOwner, config, ciFlag) + + if ciFlag { + progress.Progress.Quit() } return nil diff --git a/cmd/k3d/vault.go b/cmd/k3d/vault.go index 485e49bb..a044b115 100644 --- a/cmd/k3d/vault.go +++ b/cmd/k3d/vault.go @@ -33,7 +33,7 @@ const ( secretThreshold = 3 ) -func unsealVault(cmd *cobra.Command, args []string) error { +func unsealVault(_ *cobra.Command, _ []string) error { flags := utils.GetClusterStatusFlags() if !flags.SetupComplete { return fmt.Errorf("failed to unseal vault: there doesn't appear to be an active k3d cluster") diff --git a/cmd/k3s/command.go b/cmd/k3s/command.go index 7fda362f..48b7c28f 100644 --- a/cmd/k3s/command.go +++ b/cmd/k3s/command.go @@ -25,8 +25,8 @@ var ( clusterTypeFlag string k3sServersPrivateIpsFlag []string k3sServersPublicIpsFlag []string - k3sSshUserflag string - k3sSshPrivateKeyflag string + k3sSSHUserflag string + k3sSSHPrivateKeyflag string K3sServersArgsFlags []string dnsProviderFlag string subdomainNameFlag string @@ -93,8 +93,8 @@ func Create() *cobra.Command { createCmd.MarkFlagRequired("servers-private-ips") createCmd.Flags().StringSliceVar(&k3sServersPublicIpsFlag, "servers-public-ips", []string{}, "the list of k3s (servers) public ip x.x.x.x,y.y.y.y comma separated (required)") createCmd.Flags().StringSliceVar(&K3sServersArgsFlags, "servers-args", []string{"--disable traefik", "--write-kubeconfig-mode 644"}, "list of k3s extras args to add to the k3s server installation,comma separated in between quote, if --servers-public-ips --tls-san is added to default --servers-args") - createCmd.Flags().StringVar(&k3sSshUserflag, "ssh-user", "root", "the user used to log into servers with ssh connection") - createCmd.Flags().StringVar(&k3sSshPrivateKeyflag, "ssh-privatekey", "", "the private key used to log into servers with ssh connection") + createCmd.Flags().StringVar(&k3sSSHUserflag, "ssh-user", "root", "the user used to log into servers with ssh connection") + createCmd.Flags().StringVar(&k3sSSHPrivateKeyflag, "ssh-privatekey", "", "the private key used to log into servers with ssh connection") createCmd.MarkFlagRequired("ssh-privatekey") createCmd.Flags().StringVar(&dnsProviderFlag, "dns-provider", "cloudflare", fmt.Sprintf("the dns provider - one of: %q", supportedDNSProviders)) createCmd.Flags().StringVar(&subdomainNameFlag, "subdomain", "", "the subdomain to use for DNS records (Cloudflare)") diff --git a/cmd/k3s/create.go b/cmd/k3s/create.go index c1e87ae4..ae7ce75c 100644 --- a/cmd/k3s/create.go +++ b/cmd/k3s/create.go @@ -25,10 +25,10 @@ import ( "github.com/konstructio/kubefirst/internal/utilities" "github.com/spf13/cobra" "github.com/spf13/viper" - _ "k8s.io/client-go/plugin/pkg/client/auth" + _ "k8s.io/client-go/plugin/pkg/client/auth" // required for k8s authentication ) -func createK3s(cmd *cobra.Command, args []string) error { +func createK3s(cmd *cobra.Command, _ []string) error { cliFlags, err := utilities.GetFlags(cmd, "k3s") if err != nil { progress.Error(err.Error()) @@ -99,7 +99,7 @@ func createK3s(cmd *cobra.Command, args []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("app availability check failed: %w", err) diff --git a/cmd/launch.go b/cmd/launch.go index 89d963b8..88b23c5a 100644 --- a/cmd/launch.go +++ b/cmd/launch.go @@ -35,7 +35,7 @@ func launchUp() *cobra.Command { Use: "up", Short: "launch new console and api instance", TraverseChildren: true, - Run: func(cmd *cobra.Command, _ []string) { + Run: func(_ *cobra.Command, _ []string) { launch.Up(additionalHelmFlags, false, true) }, } @@ -51,7 +51,7 @@ func launchDown() *cobra.Command { Use: "down", Short: "remove console and api instance", TraverseChildren: true, - Run: func(cmd *cobra.Command, _ []string) { + Run: func(_ *cobra.Command, _ []string) { launch.Down(false) }, } @@ -78,7 +78,7 @@ func launchListClusters() *cobra.Command { Use: "list", Short: "list clusters created by the Kubefirst console", TraverseChildren: true, - Run: func(cmd *cobra.Command, _ []string) { + Run: func(_ *cobra.Command, _ []string) { launch.ListClusters() }, } @@ -98,7 +98,7 @@ func launchDeleteCluster() *cobra.Command { } return nil }, - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, args []string) { launch.DeleteCluster(args[0]) }, } diff --git a/cmd/letsencrypt.go b/cmd/letsencrypt.go index 1075338c..1a1071d5 100644 --- a/cmd/letsencrypt.go +++ b/cmd/letsencrypt.go @@ -35,7 +35,7 @@ func status() *cobra.Command { Use: "status", Short: "check the usage statistics for a LetsEncrypt certificate", TraverseChildren: true, - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { if err := certificates.CheckCertificateUsage(domainNameFlag); err != nil { fmt.Printf("failed to check certificate usage for domain %q: %s\n", domainNameFlag, err) } diff --git a/cmd/logs.go b/cmd/logs.go index 24355df0..426db037 100755 --- a/cmd/logs.go +++ b/cmd/logs.go @@ -21,7 +21,7 @@ var logsCmd = &cobra.Command{ Use: "logs", Short: "kubefirst real time logs", Long: `kubefirst real time logs`, - RunE: func(cmd *cobra.Command, _ []string) error { + RunE: func(_ *cobra.Command, _ []string) error { provisionLogs.InitializeProvisionLogsTerminal() go func() { diff --git a/cmd/reset.go b/cmd/reset.go index e26789cf..25312fab 100755 --- a/cmd/reset.go +++ b/cmd/reset.go @@ -25,7 +25,7 @@ var resetCmd = &cobra.Command{ Use: "reset", Short: "removes local kubefirst content to provision a new platform", Long: "removes local kubefirst content to provision a new platform", - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, _ []string) error { gitProvider := viper.GetString("kubefirst.git-provider") cloudProvider := viper.GetString("kubefirst.cloud-provider") diff --git a/cmd/root.go b/cmd/root.go index 36411b4e..88659b29 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -31,7 +31,7 @@ var rootCmd = &cobra.Command{ // wire viper config for flags for all commands return configs.InitializeViperConfig(cmd) }, - Run: func(cmd *cobra.Command, _ []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about kubefirst, run:") fmt.Println(" kubefirst help") progress.Progress.Quit() diff --git a/cmd/terraform.go b/cmd/terraform.go index 54f75cd5..ec50bedb 100644 --- a/cmd/terraform.go +++ b/cmd/terraform.go @@ -40,7 +40,7 @@ func terraformSetEnv() *cobra.Command { Use: "set-env", Short: "retrieve data from a target vault secret and format it for use in the local shell via environment variables", TraverseChildren: true, - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { v := vault.VaultConfiguration{ Config: vault.NewVault(), } diff --git a/cmd/version.go b/cmd/version.go index 4b5e6775..f446bb0e 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -20,7 +20,7 @@ var versionCmd = &cobra.Command{ Use: "version", Short: "print the version number for kubefirst-cli", Long: `All software has versions. This is kubefirst's`, - Run: func(cmd *cobra.Command, _ []string) { + Run: func(_ *cobra.Command, _ []string) { versionMsg := ` ## ### kubefirst-cli golang utility version: ` + configs.K1Version diff --git a/cmd/vultr/command.go b/cmd/vultr/command.go index 1958a4f9..3490f6dd 100644 --- a/cmd/vultr/command.go +++ b/cmd/vultr/command.go @@ -54,7 +54,7 @@ func NewCommand() *cobra.Command { Use: "vultr", Short: "Kubefirst Vultr installation", Long: "kubefirst vultr", - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Println("To learn more about Vultr in Kubefirst, run:") fmt.Println(" kubefirst beta vultr --help") diff --git a/cmd/vultr/create.go b/cmd/vultr/create.go index 26a4233d..5066b5c2 100644 --- a/cmd/vultr/create.go +++ b/cmd/vultr/create.go @@ -22,12 +22,11 @@ import ( "github.com/konstructio/kubefirst/internal/provision" "github.com/konstructio/kubefirst/internal/utilities" "github.com/rs/zerolog/log" - "github.com/spf13/cobra" "github.com/spf13/viper" ) -func createVultr(cmd *cobra.Command, args []string) error { +func createVultr(cmd *cobra.Command, _ []string) error { cliFlags, err := utilities.GetFlags(cmd, "vultr") if err != nil { progress.Error(err.Error()) @@ -97,7 +96,7 @@ func createVultr(cmd *cobra.Command, args []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresUrl()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("kubefirst api availability check failed: %w", err) diff --git a/go.mod b/go.mod index 27aba6c9..a84cb186 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/konstructio/kubefirst -go 1.18 +go 1.23 require ( github.com/argoproj/argo-cd/v2 v2.6.7 diff --git a/internal/cluster/cluster.go b/internal/cluster/cluster.go index f34b2251..e9630376 100644 --- a/internal/cluster/cluster.go +++ b/internal/cluster/cluster.go @@ -21,7 +21,7 @@ import ( "github.com/konstructio/kubefirst/internal/types" ) -func GetConsoleIngresUrl() string { +func GetConsoleIngresURL() string { if strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" { // allow using local console running on port 3000 return os.Getenv("K1_CONSOLE_REMOTE_URL") } @@ -35,7 +35,7 @@ func CreateCluster(cluster apiTypes.ClusterDefinition) error { requestObject := types.ProxyCreateClusterRequest{ Body: cluster, - Url: fmt.Sprintf("/cluster/%s", cluster.ClusterName), + URL: fmt.Sprintf("/cluster/%s", cluster.ClusterName), } payload, err := json.Marshal(requestObject) @@ -43,7 +43,7 @@ func CreateCluster(cluster apiTypes.ClusterDefinition) error { return fmt.Errorf("failed to marshal request object: %w", err) } - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngresUrl()), bytes.NewReader(payload)) + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngresURL()), bytes.NewReader(payload)) if err != nil { log.Printf("error creating request: %s", err) return fmt.Errorf("failed to create request: %w", err) @@ -79,7 +79,7 @@ func ResetClusterProgress(clusterName string) error { httpClient := http.Client{Transport: customTransport} requestObject := types.ProxyResetClusterRequest{ - Url: fmt.Sprintf("/cluster/%s/reset_progress", clusterName), + URL: fmt.Sprintf("/cluster/%s/reset_progress", clusterName), } payload, err := json.Marshal(requestObject) @@ -87,7 +87,7 @@ func ResetClusterProgress(clusterName string) error { return fmt.Errorf("failed to marshal request object: %w", err) } - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngresUrl()), bytes.NewReader(payload)) + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngresURL()), bytes.NewReader(payload)) if err != nil { log.Printf("error creating request: %v", err) return fmt.Errorf("failed to create request: %w", err) @@ -122,7 +122,7 @@ func GetCluster(clusterName string) (apiTypes.Cluster, error) { httpClient := http.Client{Transport: customTransport} cluster := apiTypes.Cluster{} - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngresUrl(), clusterName), nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngresURL(), clusterName), nil) if err != nil { log.Printf("error creating request: %v", err) return cluster, fmt.Errorf("failed to create request: %w", err) @@ -162,7 +162,7 @@ func GetClusters() ([]apiTypes.Cluster, error) { httpClient := http.Client{Transport: customTransport} clusters := []apiTypes.Cluster{} - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster", GetConsoleIngresUrl()), nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster", GetConsoleIngresURL()), nil) if err != nil { log.Printf("error creating request: %v", err) return clusters, fmt.Errorf("failed to create request: %w", err) @@ -201,7 +201,7 @@ func DeleteCluster(clusterName string) error { customTransport := http.DefaultTransport.(*http.Transport).Clone() httpClient := http.Client{Transport: customTransport} - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngresUrl(), clusterName), nil) + req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngresURL(), clusterName), nil) if err != nil { log.Printf("error creating request: %v", err) return fmt.Errorf("failed to create request: %w", err) @@ -214,6 +214,7 @@ func DeleteCluster(clusterName string) error { log.Printf("error executing request: %v", err) return fmt.Errorf("failed to execute request: %w", err) } + defer res.Body.Close() if res.StatusCode != http.StatusOK { log.Printf("unable to delete cluster: %q, continuing", res.Status) diff --git a/internal/common/common.go b/internal/common/common.go index be94af31..e95ac74e 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -58,7 +58,7 @@ func CheckForVersionUpdate() { } // versionCheck compares local to remote version -func versionCheck() (res *CheckResponse, skip bool) { +func versionCheck() (*CheckResponse, bool) { var latestVersion string flatVersion := strings.ReplaceAll(configs.K1Version, "v", "") @@ -116,7 +116,7 @@ func GetRootCredentials(_ *cobra.Command, _ []string) error { return nil } -func Destroy(cmd *cobra.Command, args []string) error { +func Destroy(_ *cobra.Command, _ []string) error { // Determine if there are active installs gitProvider := viper.GetString("flags.git-provider") gitProtocol := viper.GetString("flags.git-protocol") @@ -128,7 +128,7 @@ func Destroy(cmd *cobra.Command, args []string) error { domainName := viper.GetString("flags.domain-name") // Switch based on git provider, set params - cGitOwner := "" + var cGitOwner string switch gitProvider { case "github": cGitOwner = viper.GetString("flags.github-owner") diff --git a/internal/gitShim/containerRegistryAuth.go b/internal/gitShim/containerRegistryAuth.go index f497a3e8..6a0b6484 100644 --- a/internal/gitShim/containerRegistryAuth.go +++ b/internal/gitShim/containerRegistryAuth.go @@ -4,7 +4,7 @@ Copyright (C) 2021-2023, Kubefirst This program is licensed under MIT. See the LICENSE file for more details. */ -package gitShim +package gitShim //nolint:revive // allowed during refactoring import ( "encoding/base64" @@ -34,7 +34,6 @@ type ContainerRegistryAuth struct { func CreateContainerRegistrySecret(obj *ContainerRegistryAuth) (string, error) { // Handle secret creation for container registry authentication switch obj.GitProvider { - // GitHub docker auth secret // kaniko requires a specific format for Docker auth created as a secret // For GitHub, this becomes the provided token (pat) diff --git a/internal/gitShim/init.go b/internal/gitShim/init.go index 124bffe7..c60a05a7 100644 --- a/internal/gitShim/init.go +++ b/internal/gitShim/init.go @@ -4,7 +4,7 @@ Copyright (C) 2021-2023, Kubefirst This program is licensed under MIT. See the LICENSE file for more details. */ -package gitShim +package gitShim //nolint:revive // allowed during refactoring import ( "errors" diff --git a/internal/helm/types.go b/internal/helm/types.go index 66405985..1845390c 100644 --- a/internal/helm/types.go +++ b/internal/helm/types.go @@ -6,12 +6,12 @@ See the LICENSE file for more details. */ package helm -type HelmRepo struct { +type Repo struct { Name string `yaml:"name"` URL string `yaml:"url"` } -type HelmRelease struct { +type Release struct { AppVersion string `yaml:"app_version"` Chart string `yaml:"chart"` Name string `yaml:"name"` diff --git a/internal/k3d/menu.go b/internal/k3d/menu.go index 76ccc29c..662ba587 100644 --- a/internal/k3d/menu.go +++ b/internal/k3d/menu.go @@ -37,9 +37,9 @@ func (i Item) FilterValue() string { return "" } type ItemDelegate struct{} -func (d ItemDelegate) Height() int { return 1 } -func (d ItemDelegate) Spacing() int { return 0 } -func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } +func (d ItemDelegate) Height() int { return 1 } +func (d ItemDelegate) Spacing() int { return 0 } +func (d ItemDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil } func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { i, ok := listItem.(Item) if !ok { @@ -104,7 +104,7 @@ func (m Model) View() string { return "\n" + m.List.View() } -func MongoDestinationChooser(inCluster bool) (result string, err error) { +func MongoDestinationChooser(inCluster bool) (string, error) { if inCluster { return "in-cluster", nil } diff --git a/internal/launch/cmd.go b/internal/launch/cmd.go index 41661cf0..0463dde9 100644 --- a/internal/launch/cmd.go +++ b/internal/launch/cmd.go @@ -73,13 +73,13 @@ func Up(additionalHelmFlags []string, inCluster, useTelemetry bool) { _, err = os.Stat(k3dClient) if err != nil { log.Info().Msg("Downloading k3d...") - k3dDownloadUrl := fmt.Sprintf( + k3dDownloadURL := fmt.Sprintf( "https://github.com/k3d-io/k3d/releases/download/%s/k3d-%s-%s", k3d.K3dVersion, k3d.LocalhostOS, k3d.LocalhostARCH, ) - err = downloadManager.DownloadFile(k3dClient, k3dDownloadUrl) + err = downloadManager.DownloadFile(k3dClient, k3dDownloadURL) if err != nil { progress.Error(fmt.Sprintf("error while trying to download k3d: %s", err)) return @@ -100,14 +100,14 @@ func Up(additionalHelmFlags []string, inCluster, useTelemetry bool) { if err != nil { log.Info().Msg("Downloading helm...") helmVersion := "v3.12.0" - helmDownloadUrl := fmt.Sprintf( + helmDownloadURL := fmt.Sprintf( "https://get.helm.sh/helm-%s-%s-%s.tar.gz", helmVersion, k3d.LocalhostOS, k3d.LocalhostARCH, ) helmDownloadTarGzPath := fmt.Sprintf("%s/helm.tar.gz", toolsDir) - err = downloadManager.DownloadFile(helmDownloadTarGzPath, helmDownloadUrl) + err = downloadManager.DownloadFile(helmDownloadTarGzPath, helmDownloadURL) if err != nil { progress.Error(fmt.Sprintf("error while trying to download helm: %s", err)) return @@ -223,7 +223,7 @@ func Up(additionalHelmFlags []string, inCluster, useTelemetry bool) { return } - var existingHelmRepositories []helm.HelmRepo + var existingHelmRepositories []helm.Repo repoExists := false err = yaml.Unmarshal([]byte(res), &existingHelmRepositories) @@ -283,7 +283,7 @@ func Up(additionalHelmFlags []string, inCluster, useTelemetry bool) { return } - var existingHelmReleases []helm.HelmRelease + var existingHelmReleases []helm.Release chartInstalled := false err = yaml.Unmarshal([]byte(res), &existingHelmReleases) @@ -429,7 +429,7 @@ func Up(additionalHelmFlags []string, inCluster, useTelemetry bool) { return } - //* read certificate files + // * read certificate files certPem, err := os.ReadFile(fmt.Sprintf("%s/%s-cert.pem", mkcertPemDir, "kubefirst-console")) if err != nil { progress.Error(fmt.Sprintf("error reading certificate for console: %s", err)) diff --git a/internal/progress/command.go b/internal/progress/command.go index 7d69d355..a76a3e4b 100644 --- a/internal/progress/command.go +++ b/internal/progress/command.go @@ -17,7 +17,7 @@ import ( // Commands func GetClusterInterval(clusterName string) tea.Cmd { - return tea.Every(time.Second*10, func(t time.Time) tea.Msg { + return tea.Every(time.Second*10, func(_ time.Time) tea.Msg { provisioningCluster, err := cluster.GetCluster(clusterName) if err != nil { log.Printf("failed to get cluster %q: %v", clusterName, err) @@ -29,70 +29,70 @@ func GetClusterInterval(clusterName string) tea.Cmd { } func AddSuccesMessage(cluster types.Cluster) tea.Cmd { - return tea.Tick(0, func(t time.Time) tea.Msg { + return tea.Tick(0, func(_ time.Time) tea.Msg { successMessage := DisplaySuccessMessage(cluster) - return successMsg(successMessage) + return successMessage }) } -func BuildCompletedSteps(cluster types.Cluster, model progressModel) ([]string, string) { +func BuildCompletedSteps(cluster types.Cluster) ([]string, string) { completedSteps := []string{} nextStep := "" if cluster.InstallToolsCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.install_tools_check) - nextStep = CompletedStepsLabels.domain_liveness_check + completedSteps = append(completedSteps, CompletedStepsLabels.installToolsCheck) + nextStep = CompletedStepsLabels.domainLivenessCheck } if cluster.DomainLivenessCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.domain_liveness_check) - nextStep = CompletedStepsLabels.kbot_setup_check + completedSteps = append(completedSteps, CompletedStepsLabels.domainLivenessCheck) + nextStep = CompletedStepsLabels.kbotSetupCheck } if cluster.KbotSetupCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.kbot_setup_check) - nextStep = CompletedStepsLabels.git_init_check + completedSteps = append(completedSteps, CompletedStepsLabels.kbotSetupCheck) + nextStep = CompletedStepsLabels.gitInitCheck } if cluster.GitInitCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.git_init_check) - nextStep = CompletedStepsLabels.gitops_ready_check + completedSteps = append(completedSteps, CompletedStepsLabels.gitInitCheck) + nextStep = CompletedStepsLabels.gitopsReadyCheck } if cluster.GitopsReadyCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.gitops_ready_check) - nextStep = CompletedStepsLabels.git_terraform_apply_check + completedSteps = append(completedSteps, CompletedStepsLabels.gitopsReadyCheck) + nextStep = CompletedStepsLabels.gitTerraformApplyCheck } if cluster.GitTerraformApplyCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.git_terraform_apply_check) - nextStep = CompletedStepsLabels.gitops_pushed_check + completedSteps = append(completedSteps, CompletedStepsLabels.gitTerraformApplyCheck) + nextStep = CompletedStepsLabels.gitopsPushedCheck } if cluster.GitopsPushedCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.gitops_pushed_check) - nextStep = CompletedStepsLabels.cloud_terraform_apply_check + completedSteps = append(completedSteps, CompletedStepsLabels.gitopsPushedCheck) + nextStep = CompletedStepsLabels.cloudTerraformApplyCheck } if cluster.CloudTerraformApplyCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.cloud_terraform_apply_check) - nextStep = CompletedStepsLabels.cluster_secrets_created_check + completedSteps = append(completedSteps, CompletedStepsLabels.cloudTerraformApplyCheck) + nextStep = CompletedStepsLabels.clusterSecretsCreatedCheck } if cluster.ClusterSecretsCreatedCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.cluster_secrets_created_check) - nextStep = CompletedStepsLabels.argocd_install_check + completedSteps = append(completedSteps, CompletedStepsLabels.clusterSecretsCreatedCheck) + nextStep = CompletedStepsLabels.argoCDInstallCheck } if cluster.ArgoCDInstallCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.argocd_install_check) - nextStep = CompletedStepsLabels.argocd_initialize_check + completedSteps = append(completedSteps, CompletedStepsLabels.argoCDInstallCheck) + nextStep = CompletedStepsLabels.argoCDInitializeCheck } if cluster.ArgoCDInitializeCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.argocd_initialize_check) - nextStep = CompletedStepsLabels.vault_initialized_check + completedSteps = append(completedSteps, CompletedStepsLabels.argoCDInitializeCheck) + nextStep = CompletedStepsLabels.vaultInitializedCheck } if cluster.VaultInitializedCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.vault_initialized_check) - nextStep = CompletedStepsLabels.vault_terraform_apply_check + completedSteps = append(completedSteps, CompletedStepsLabels.vaultInitializedCheck) + nextStep = CompletedStepsLabels.vaultTerraformApplyCheck } if cluster.VaultTerraformApplyCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.vault_terraform_apply_check) - nextStep = CompletedStepsLabels.users_terraform_apply_check + completedSteps = append(completedSteps, CompletedStepsLabels.vaultTerraformApplyCheck) + nextStep = CompletedStepsLabels.usersTerraformApplyCheck } if cluster.UsersTerraformApplyCheck { - completedSteps = append(completedSteps, CompletedStepsLabels.users_terraform_apply_check) + completedSteps = append(completedSteps, CompletedStepsLabels.usersTerraformApplyCheck) nextStep = "Wrapping up" } diff --git a/internal/progress/constants.go b/internal/progress/constants.go index 41335b83..a3dc7d7d 100644 --- a/internal/progress/constants.go +++ b/internal/progress/constants.go @@ -6,41 +6,19 @@ See the LICENSE file for more details. */ package progress -import ( - "time" - - "github.com/charmbracelet/lipgloss" -) - -const ( - padding = 2 - maxWidth = 80 -) - -const debounceDuration = time.Second * 10 - -var ( - currentPkgNameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211")) - doneStyle = lipgloss.NewStyle().Margin(1, 2) - checkMark = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).SetString("✓") - helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")).Render - StatusStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF")).Bold(true).Render - spinnerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("69")) -) - var CompletedStepsLabels = ProvisionSteps{ - install_tools_check: "Installing tools", - domain_liveness_check: "Domain liveness check", - kbot_setup_check: "Kbot setup", - git_init_check: "Initializing Git", - gitops_ready_check: "Initializing GitOps", - git_terraform_apply_check: "Git Terraform apply", - gitops_pushed_check: "GitOps repos pushed", - cloud_terraform_apply_check: "Cloud Terraform apply", - cluster_secrets_created_check: "Creating cluster secrets", - argocd_install_check: "Installing ArgoCD", - argocd_initialize_check: "Initializing ArgoCD", - vault_initialized_check: "Initializing Vault", - vault_terraform_apply_check: "Vault Terraform apply", - users_terraform_apply_check: "Users Terraform apply", + installToolsCheck: "Installing tools", + domainLivenessCheck: "Domain liveness check", + kbotSetupCheck: "Kbot setup", + gitInitCheck: "Initializing Git", + gitopsReadyCheck: "Initializing GitOps", + gitTerraformApplyCheck: "Git Terraform apply", + gitopsPushedCheck: "GitOps repos pushed", + cloudTerraformApplyCheck: "Cloud Terraform apply", + clusterSecretsCreatedCheck: "Creating cluster secrets", + argoCDInstallCheck: "Installing ArgoCD", + argoCDInitializeCheck: "Initializing ArgoCD", + vaultInitializedCheck: "Initializing Vault", + vaultTerraformApplyCheck: "Vault Terraform apply", + usersTerraformApplyCheck: "Users Terraform apply", } diff --git a/internal/progress/message.go b/internal/progress/message.go index daceb3aa..d016eaf0 100644 --- a/internal/progress/message.go +++ b/internal/progress/message.go @@ -76,6 +76,7 @@ func DisplayLogHints(estimatedTime int) { }) } +//nolint:revive // will be fixed in the future func DisplaySuccessMessage(cluster types.Cluster) successMsg { cloudCliKubeconfig := "" diff --git a/internal/progress/progress.go b/internal/progress/progress.go index b33d9ba7..6d0b42c7 100644 --- a/internal/progress/progress.go +++ b/internal/progress/progress.go @@ -16,6 +16,7 @@ import ( var Progress *tea.Program +//nolint:revive // will be removed after refactoring func NewModel() progressModel { return progressModel{ isProvisioned: false, @@ -68,7 +69,7 @@ func (m progressModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case CusterProvisioningMsg: m.provisioningCluster = types.Cluster(msg) - completedSteps, nextStep := BuildCompletedSteps(types.Cluster(msg), m) + completedSteps, nextStep := BuildCompletedSteps(types.Cluster(msg)) m.completedSteps = append(m.completedSteps, completedSteps...) m.nextStep = renderMessage(fmt.Sprintf(":dizzy: %s", nextStep)) @@ -104,7 +105,7 @@ func (m progressModel) View() string { completedSteps := "" for i := index; i < len(m.completedSteps); i++ { - completedSteps = completedSteps + renderMessage(fmt.Sprintf(":white_check_mark: %s", m.completedSteps[i])) + completedSteps += renderMessage(fmt.Sprintf(":white_check_mark: %s", m.completedSteps[i])) } if m.header != "" { diff --git a/internal/progress/types.go b/internal/progress/types.go index ac276fbe..25826019 100644 --- a/internal/progress/types.go +++ b/internal/progress/types.go @@ -57,18 +57,18 @@ type successMsg struct { // Custom type ProvisionSteps struct { - install_tools_check string - domain_liveness_check string - kbot_setup_check string - git_init_check string - gitops_ready_check string - git_terraform_apply_check string - gitops_pushed_check string - cloud_terraform_apply_check string - cluster_secrets_created_check string - argocd_install_check string - argocd_initialize_check string - vault_initialized_check string - vault_terraform_apply_check string - users_terraform_apply_check string + installToolsCheck string + domainLivenessCheck string + kbotSetupCheck string + gitInitCheck string + gitopsReadyCheck string + gitTerraformApplyCheck string + gitopsPushedCheck string + cloudTerraformApplyCheck string + clusterSecretsCreatedCheck string + argoCDInstallCheck string + argoCDInitializeCheck string + vaultInitializedCheck string + vaultTerraformApplyCheck string + usersTerraformApplyCheck string } diff --git a/internal/provisionLogs/command.go b/internal/provisionLogs/command.go index e8b2c739..47859bf0 100644 --- a/internal/provisionLogs/command.go +++ b/internal/provisionLogs/command.go @@ -4,7 +4,7 @@ Copyright (C) 2021-2023, Kubefirst This program is licensed under MIT. See the LICENSE file for more details. */ -package provisionLogs +package provisionLogs //nolint:revive // allowed during refactoring import ( "encoding/json" diff --git a/internal/provisionLogs/message.go b/internal/provisionLogs/message.go deleted file mode 100644 index 1b5ec504..00000000 --- a/internal/provisionLogs/message.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright (C) 2021-2023, Kubefirst - -This program is licensed under MIT. -See the LICENSE file for more details. - -Emojis definition https://github.com/yuin/goldmark-emoji/blob/master/definition/github.go -Color definition https://www.ditig.com/256-colors-cheat-sheet -*/ -package provisionLogs - -import ( - "fmt" - "log" - - "github.com/charmbracelet/glamour" - "github.com/konstructio/kubefirst/internal/progress" -) - -func renderMessage(message string) string { - r, _ := glamour.NewTermRenderer( - glamour.WithStyles(progress.StyleConfig), - glamour.WithEmoji(), - ) - - out, err := r.Render(message) - if err != nil { - s := fmt.Errorf("rendering message failed: %w", err) - log.Println(s) - return s.Error() - } - return out -} diff --git a/internal/provisionLogs/provisionLogs.go b/internal/provisionLogs/provisionLogs.go index 4b63fb7e..b2618e8c 100644 --- a/internal/provisionLogs/provisionLogs.go +++ b/internal/provisionLogs/provisionLogs.go @@ -4,7 +4,7 @@ Copyright (C) 2021-2023, Kubefirst This program is licensed under MIT. See the LICENSE file for more details. */ -package provisionLogs +package provisionLogs //nolint:revive // allowed during refactoring import ( tea "github.com/charmbracelet/bubbletea" @@ -15,6 +15,7 @@ var quitStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")).Render var ProvisionLogs *tea.Program +//nolint:revive // will be removed after refactoring func NewModel() provisionLogsModel { return provisionLogsModel{} } diff --git a/internal/provisionLogs/types.go b/internal/provisionLogs/types.go index a67905f2..3dd3906c 100644 --- a/internal/provisionLogs/types.go +++ b/internal/provisionLogs/types.go @@ -4,7 +4,7 @@ Copyright (C) 2021-2023, Kubefirst This program is licensed under MIT. See the LICENSE file for more details. */ -package provisionLogs +package provisionLogs //nolint:revive // allowed during refactoring // Terminal model type provisionLogsModel struct { diff --git a/internal/segment/segment.go b/internal/segment/segment.go index 927b7c3e..ba6616d7 100644 --- a/internal/segment/segment.go +++ b/internal/segment/segment.go @@ -14,7 +14,7 @@ const ( kubefirstClient string = "api" ) -func InitClient(clusterId, clusterType, gitProvider string) (telemetry.TelemetryEvent, error) { +func InitClient(clusterID, clusterType, gitProvider string) (telemetry.TelemetryEvent, error) { machineID, err := machineid.ID() if err != nil { return telemetry.TelemetryEvent{}, fmt.Errorf("failed to get machine ID: %w", err) @@ -23,7 +23,7 @@ func InitClient(clusterId, clusterType, gitProvider string) (telemetry.Telemetry c := telemetry.TelemetryEvent{ CliVersion: configs.K1Version, CloudProvider: k3d.CloudProvider, - ClusterID: clusterId, + ClusterID: clusterID, ClusterType: clusterType, DomainName: k3d.DomainName, GitProvider: gitProvider, diff --git a/internal/types/flags.go b/internal/types/flags.go index 43795f15..84c3777d 100644 --- a/internal/types/flags.go +++ b/internal/types/flags.go @@ -13,7 +13,7 @@ type CliFlags struct { CloudProvider string ClusterName string ClusterType string - DnsProvider string + DNSProvider string DomainName string SubDomainName string GitProvider string @@ -24,14 +24,14 @@ type CliFlags struct { GitopsTemplateURL string GoogleProject string UseTelemetry bool - Ecr bool + ECR bool NodeType string NodeCount string InstallCatalogApps string - K3sSshUser string - K3sSshPrivateKey string - K3sServersPrivateIps []string - K3sServersPublicIps []string + K3sSSHUser string + K3sSSHPrivateKey string + K3sServersPrivateIPs []string + K3sServersPublicIPs []string K3sServersArgs []string InstallKubefirstPro bool } diff --git a/internal/types/proxy.go b/internal/types/proxy.go index dc16ceb2..a4b444d1 100644 --- a/internal/types/proxy.go +++ b/internal/types/proxy.go @@ -12,9 +12,9 @@ import ( type ProxyCreateClusterRequest struct { Body apiTypes.ClusterDefinition `bson:"body" json:"body"` - Url string `bson:"url" json:"url"` + URL string `bson:"url" json:"url"` } type ProxyResetClusterRequest struct { - Url string `bson:"url" json:"url"` + URL string `bson:"url" json:"url"` } diff --git a/internal/utilities/flags.go b/internal/utilities/flags.go index ed0ff19c..12306966 100644 --- a/internal/utilities/flags.go +++ b/internal/utilities/flags.go @@ -130,7 +130,7 @@ func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) return cliFlags, fmt.Errorf("failed to get ecr flag: %w", err) } - cliFlags.Ecr = ecrFlag + cliFlags.ECR = ecrFlag } if cloudProvider == "google" { @@ -149,28 +149,28 @@ func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) progress.Error(err.Error()) return cliFlags, fmt.Errorf("failed to get servers-private-ips flag: %w", err) } - cliFlags.K3sServersPrivateIps = k3sServersPrivateIps + cliFlags.K3sServersPrivateIPs = k3sServersPrivateIps k3sServersPublicIps, err := cmd.Flags().GetStringSlice("servers-public-ips") if err != nil { progress.Error(err.Error()) return cliFlags, fmt.Errorf("failed to get servers-public-ips flag: %w", err) } - cliFlags.K3sServersPublicIps = k3sServersPublicIps + cliFlags.K3sServersPublicIPs = k3sServersPublicIps - k3sSshUserFlag, err := cmd.Flags().GetString("ssh-user") + k3sSSHUserFlag, err := cmd.Flags().GetString("ssh-user") if err != nil { progress.Error(err.Error()) return cliFlags, fmt.Errorf("failed to get ssh-user flag: %w", err) } - cliFlags.K3sSshUser = k3sSshUserFlag + cliFlags.K3sSSHUser = k3sSSHUserFlag - k3sSshPrivateKeyFlag, err := cmd.Flags().GetString("ssh-privatekey") + k3sSSHPrivateKeyFlag, err := cmd.Flags().GetString("ssh-privatekey") if err != nil { progress.Error(err.Error()) return cliFlags, fmt.Errorf("failed to get ssh-privatekey flag: %w", err) } - cliFlags.K3sSshPrivateKey = k3sSshPrivateKeyFlag + cliFlags.K3sSSHPrivateKey = k3sSSHPrivateKeyFlag K3sServersArgsFlags, err := cmd.Flags().GetStringSlice("servers-args") if err != nil { @@ -183,7 +183,7 @@ func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) cliFlags.AlertsEmail = alertsEmailFlag cliFlags.CloudRegion = cloudRegionFlag cliFlags.ClusterName = clusterNameFlag - cliFlags.DnsProvider = dnsProviderFlag + cliFlags.DNSProvider = dnsProviderFlag cliFlags.SubDomainName = subdomainFlag cliFlags.DomainName = domainNameFlag cliFlags.GitProtocol = gitProtocolFlag @@ -201,17 +201,17 @@ func GetFlags(cmd *cobra.Command, cloudProvider string) (types.CliFlags, error) viper.Set("flags.alerts-email", cliFlags.AlertsEmail) viper.Set("flags.cluster-name", cliFlags.ClusterName) - viper.Set("flags.dns-provider", cliFlags.DnsProvider) + viper.Set("flags.dns-provider", cliFlags.DNSProvider) viper.Set("flags.domain-name", cliFlags.DomainName) viper.Set("flags.git-provider", cliFlags.GitProvider) viper.Set("flags.git-protocol", cliFlags.GitProtocol) viper.Set("flags.cloud-region", cliFlags.CloudRegion) viper.Set("kubefirst.cloud-provider", cloudProvider) if cloudProvider == "k3s" { - viper.Set("flags.servers-private-ips", cliFlags.K3sServersPrivateIps) - viper.Set("flags.servers-public-ips", cliFlags.K3sServersPublicIps) - viper.Set("flags.ssh-user", cliFlags.K3sSshUser) - viper.Set("flags.ssh-privatekey", cliFlags.K3sSshPrivateKey) + viper.Set("flags.servers-private-ips", cliFlags.K3sServersPrivateIPs) + viper.Set("flags.servers-public-ips", cliFlags.K3sServersPublicIPs) + viper.Set("flags.ssh-user", cliFlags.K3sSSHUser) + viper.Set("flags.ssh-privatekey", cliFlags.K3sSSHPrivateKey) viper.Set("flags.servers-args", cliFlags.K3sServersArgs) } if err := viper.WriteConfig(); err != nil { diff --git a/internal/utilities/utilities.go b/internal/utilities/utilities.go index 9b9b759d..bf953c1e 100644 --- a/internal/utilities/utilities.go +++ b/internal/utilities/utilities.go @@ -139,7 +139,7 @@ func CreateClusterDefinitionRecordFromRaw(gitAuth apiTypes.GitAuth, cliFlags typ kubefirstTeam := os.Getenv("KUBEFIRST_TEAM") if kubefirstTeam == "" { - kubefirstTeam = "false" + kubefirstTeam = "false" //nolint:ineffassign,wastedassign // will be fixed in the future } stringToIntNodeCount, err := strconv.Atoi(cliFlags.NodeCount) @@ -192,7 +192,7 @@ func CreateClusterDefinitionRecordFromRaw(gitAuth apiTypes.GitAuth, cliFlags typ cl.AWSAuth.AccessKeyID = viper.GetString("kubefirst.state-store-creds.access-key-id") cl.AWSAuth.SecretAccessKey = viper.GetString("kubefirst.state-store-creds.secret-access-key-id") cl.AWSAuth.SessionToken = viper.GetString("kubefirst.state-store-creds.token") - cl.ECR = cliFlags.Ecr + cl.ECR = cliFlags.ECR case "civo": cl.CivoAuth.Token = os.Getenv("CIVO_TOKEN") case "digitalocean": @@ -271,7 +271,7 @@ func ExportCluster(cluster apiTypes.Cluster, kcfg *k8s.KubernetesClient) error { func ConsumeStream(url string) { client := &http.Client{} - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { log.Error().Msgf("Error creating request: %s", err) return diff --git a/main.go b/main.go index c5e4ea0f..d7227eb5 100644 --- a/main.go +++ b/main.go @@ -12,16 +12,14 @@ import ( "os" "time" - "golang.org/x/exp/slices" - - zeroLog "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "github.com/konstructio/kubefirst-api/pkg/configs" utils "github.com/konstructio/kubefirst-api/pkg/utils" "github.com/konstructio/kubefirst/cmd" "github.com/konstructio/kubefirst/internal/progress" + zeroLog "github.com/rs/zerolog" + "github.com/rs/zerolog/log" "github.com/spf13/viper" + "golang.org/x/exp/slices" ) func main() { @@ -40,7 +38,8 @@ func main() { config := configs.ReadConfig() if err := utils.SetupViper(config, true); err != nil { - stdLog.Panic(err) + log.Error().Msgf("failed to setup Viper: %v", err) + return } now := time.Now() @@ -74,38 +73,39 @@ func main() { homePath, err := os.UserHomeDir() if err != nil { - log.Info().Msg(err.Error()) + log.Error().Msgf("failed to get user home directory: %v", err) + return } k1Dir := fmt.Sprintf("%s/.k1", homePath) - //* create k1Dir if it doesn't exist + // * create k1Dir if it doesn't exist if _, err := os.Stat(k1Dir); os.IsNotExist(err) { - err := os.MkdirAll(k1Dir, os.ModePerm) - if err != nil { - log.Info().Msgf("%s directory already exists, continuing", k1Dir) + if err := os.MkdirAll(k1Dir, os.ModePerm); err != nil { + log.Error().Msgf("error creating directory %q: %v", k1Dir, err) + return } } - //* create log directory + // * create log directory logsFolder := fmt.Sprintf("%s/logs", k1Dir) - _ = os.Mkdir(logsFolder, 0o700) - if err != nil { - log.Fatal().Msgf("error creating logs directory: %s", err) + if err := os.Mkdir(logsFolder, 0o700); err != nil { + log.Error().Msgf("error creating logs directory: %v", err) + return } - //* create session log file + // * create session log file logfile := fmt.Sprintf("%s/%s", logsFolder, logfileName) logFileObj, err := utils.OpenLogFile(logfile) if err != nil { - stdLog.Panicf("unable to store log location, error is: %s - please verify the current user has write access to this directory", err) + log.Error().Msgf("unable to store log location, error is: %v - please verify the current user has write access to this directory", err) + return } // handle file close request defer func(logFileObj *os.File) { - err = logFileObj.Close() - if err != nil { - log.Print(err) + if err := logFileObj.Close(); err != nil { + log.Error().Msgf("error closing log file: %v", err) } }(logFileObj) @@ -121,9 +121,9 @@ func main() { viper.Set("k1-paths.log-file", logfile) viper.Set("k1-paths.log-file-name", logfileName) - err = viper.WriteConfig() - if err != nil { - stdLog.Panicf("unable to set log-file-location, error is: %s", err) + if err := viper.WriteConfig(); err != nil { + log.Error().Msgf("failed to write config: %v", err) + return } if canRunBubbleTea { From debbb13d0d8341e729619135297dc2fb7f88cb14 Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:46:05 -0400 Subject: [PATCH 7/9] Fix incorrect printing statement. --- internal/utilities/utilities.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/utilities/utilities.go b/internal/utilities/utilities.go index bf953c1e..12501823 100644 --- a/internal/utilities/utilities.go +++ b/internal/utilities/utilities.go @@ -293,7 +293,7 @@ func ConsumeStream(url string) { scanner := bufio.NewScanner(resp.Body) for scanner.Scan() { data := scanner.Text() - log.Info().Msgf(data) + log.Info().Msg(data) } if err := scanner.Err(); err != nil { From 20e2ee1068f8130c954ddc675d3b55002743a06b Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:28:14 -0400 Subject: [PATCH 8/9] Fix typo in function name. --- cmd/akamai/create.go | 2 +- cmd/aws/create.go | 2 +- cmd/civo/create.go | 2 +- cmd/digitalocean/create.go | 2 +- cmd/google/create.go | 2 +- cmd/k3s/create.go | 2 +- cmd/vultr/create.go | 2 +- internal/cluster/cluster.go | 12 ++++++------ 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/akamai/create.go b/cmd/akamai/create.go index 7a5dbab6..ca65a50e 100644 --- a/cmd/akamai/create.go +++ b/cmd/akamai/create.go @@ -84,7 +84,7 @@ func createAkamai(cmd *cobra.Command, _ []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") + err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("failed to check kubefirst api availability: %w", err) diff --git a/cmd/aws/create.go b/cmd/aws/create.go index a19203af..714afa15 100644 --- a/cmd/aws/create.go +++ b/cmd/aws/create.go @@ -118,7 +118,7 @@ func createAws(cmd *cobra.Command, _ []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") + err = pkg.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("failed to check kubefirst API availability: %w", err) diff --git a/cmd/civo/create.go b/cmd/civo/create.go index d6ad36d9..79a7dbaf 100644 --- a/cmd/civo/create.go +++ b/cmd/civo/create.go @@ -87,7 +87,7 @@ func createCivo(cmd *cobra.Command, _ []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("API availability check failed: %w", err) diff --git a/cmd/digitalocean/create.go b/cmd/digitalocean/create.go index a600c619..6d848939 100644 --- a/cmd/digitalocean/create.go +++ b/cmd/digitalocean/create.go @@ -96,7 +96,7 @@ func createDigitalocean(cmd *cobra.Command, _ []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("failed to check app availability for Kubefirst API: %w", err) diff --git a/cmd/google/create.go b/cmd/google/create.go index 00fd53dc..d4e33845 100644 --- a/cmd/google/create.go +++ b/cmd/google/create.go @@ -89,7 +89,7 @@ func createGoogle(cmd *cobra.Command, _ []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("kubefirst api availability check failed: %w", err) diff --git a/cmd/k3s/create.go b/cmd/k3s/create.go index ae7ce75c..57870135 100644 --- a/cmd/k3s/create.go +++ b/cmd/k3s/create.go @@ -99,7 +99,7 @@ func createK3s(cmd *cobra.Command, _ []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("app availability check failed: %w", err) diff --git a/cmd/vultr/create.go b/cmd/vultr/create.go index 5066b5c2..5d20ca12 100644 --- a/cmd/vultr/create.go +++ b/cmd/vultr/create.go @@ -96,7 +96,7 @@ func createVultr(cmd *cobra.Command, _ []string) error { launch.Up(nil, true, cliFlags.UseTelemetry) } - err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngresURL()), "kubefirst api") + err = utils.IsAppAvailable(fmt.Sprintf("%s/api/proxyHealth", cluster.GetConsoleIngressURL()), "kubefirst api") if err != nil { progress.Error("unable to start kubefirst api") return fmt.Errorf("kubefirst api availability check failed: %w", err) diff --git a/internal/cluster/cluster.go b/internal/cluster/cluster.go index e9630376..64b5223a 100644 --- a/internal/cluster/cluster.go +++ b/internal/cluster/cluster.go @@ -21,7 +21,7 @@ import ( "github.com/konstructio/kubefirst/internal/types" ) -func GetConsoleIngresURL() string { +func GetConsoleIngressURL() string { if strings.ToLower(os.Getenv("K1_LOCAL_DEBUG")) == "true" { // allow using local console running on port 3000 return os.Getenv("K1_CONSOLE_REMOTE_URL") } @@ -43,7 +43,7 @@ func CreateCluster(cluster apiTypes.ClusterDefinition) error { return fmt.Errorf("failed to marshal request object: %w", err) } - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngresURL()), bytes.NewReader(payload)) + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngressURL()), bytes.NewReader(payload)) if err != nil { log.Printf("error creating request: %s", err) return fmt.Errorf("failed to create request: %w", err) @@ -87,7 +87,7 @@ func ResetClusterProgress(clusterName string) error { return fmt.Errorf("failed to marshal request object: %w", err) } - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngresURL()), bytes.NewReader(payload)) + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/proxy", GetConsoleIngressURL()), bytes.NewReader(payload)) if err != nil { log.Printf("error creating request: %v", err) return fmt.Errorf("failed to create request: %w", err) @@ -122,7 +122,7 @@ func GetCluster(clusterName string) (apiTypes.Cluster, error) { httpClient := http.Client{Transport: customTransport} cluster := apiTypes.Cluster{} - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngresURL(), clusterName), nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngressURL(), clusterName), nil) if err != nil { log.Printf("error creating request: %v", err) return cluster, fmt.Errorf("failed to create request: %w", err) @@ -162,7 +162,7 @@ func GetClusters() ([]apiTypes.Cluster, error) { httpClient := http.Client{Transport: customTransport} clusters := []apiTypes.Cluster{} - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster", GetConsoleIngresURL()), nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/proxy?url=/cluster", GetConsoleIngressURL()), nil) if err != nil { log.Printf("error creating request: %v", err) return clusters, fmt.Errorf("failed to create request: %w", err) @@ -201,7 +201,7 @@ func DeleteCluster(clusterName string) error { customTransport := http.DefaultTransport.(*http.Transport).Clone() httpClient := http.Client{Transport: customTransport} - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngresURL(), clusterName), nil) + req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/proxy?url=/cluster/%s", GetConsoleIngressURL(), clusterName), nil) if err != nil { log.Printf("error creating request: %v", err) return fmt.Errorf("failed to create request: %w", err) From b2d862456660444da3db5f0026ca5a6d0266237b Mon Sep 17 00:00:00 2001 From: Patrick D'appollonio <930925+patrickdappollonio@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:11:29 -0400 Subject: [PATCH 9/9] Fixes to error handling. --- cmd/akamai/create.go | 6 +++++- cmd/aws/create.go | 6 +++++- cmd/civo/create.go | 6 +++++- cmd/digitalocean/create.go | 6 +++++- cmd/google/create.go | 6 +++++- cmd/k3s/create.go | 5 ++++- cmd/vultr/create.go | 6 +++++- internal/cluster/cluster.go | 11 +++++++++-- internal/provision/provision.go | 8 ++++---- 9 files changed, 47 insertions(+), 13 deletions(-) diff --git a/cmd/akamai/create.go b/cmd/akamai/create.go index ca65a50e..68230813 100644 --- a/cmd/akamai/create.go +++ b/cmd/akamai/create.go @@ -90,7 +90,11 @@ func createAkamai(cmd *cobra.Command, _ []string) error { return fmt.Errorf("failed to check kubefirst api availability: %w", err) } - provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) + if err := provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps); err != nil { + progress.Error(err.Error()) + return fmt.Errorf("failed to create management cluster: %w", err) + } + return nil } diff --git a/cmd/aws/create.go b/cmd/aws/create.go index 714afa15..c917a98e 100644 --- a/cmd/aws/create.go +++ b/cmd/aws/create.go @@ -124,7 +124,11 @@ func createAws(cmd *cobra.Command, _ []string) error { return fmt.Errorf("failed to check kubefirst API availability: %w", err) } - provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) + if err := provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps); err != nil { + progress.Error(err.Error()) + return fmt.Errorf("failed to create management cluster: %w", err) + } + return nil } diff --git a/cmd/civo/create.go b/cmd/civo/create.go index 79a7dbaf..14592cea 100644 --- a/cmd/civo/create.go +++ b/cmd/civo/create.go @@ -93,7 +93,11 @@ func createCivo(cmd *cobra.Command, _ []string) error { return fmt.Errorf("API availability check failed: %w", err) } - provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) + if err := provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps); err != nil { + progress.Error(err.Error()) + return fmt.Errorf("failed to create management cluster: %w", err) + } + return nil } diff --git a/cmd/digitalocean/create.go b/cmd/digitalocean/create.go index 6d848939..c1213496 100644 --- a/cmd/digitalocean/create.go +++ b/cmd/digitalocean/create.go @@ -102,7 +102,11 @@ func createDigitalocean(cmd *cobra.Command, _ []string) error { return fmt.Errorf("failed to check app availability for Kubefirst API: %w", err) } - provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) + if err := provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps); err != nil { + progress.Error(err.Error()) + return fmt.Errorf("failed to create management cluster: %w", err) + } + return nil } diff --git a/cmd/google/create.go b/cmd/google/create.go index d4e33845..bb074d9b 100644 --- a/cmd/google/create.go +++ b/cmd/google/create.go @@ -95,7 +95,11 @@ func createGoogle(cmd *cobra.Command, _ []string) error { return fmt.Errorf("kubefirst api availability check failed: %w", err) } - provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) + if err := provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps); err != nil { + progress.Error(err.Error()) + return fmt.Errorf("failed to create management cluster: %w", err) + } + return nil } diff --git a/cmd/k3s/create.go b/cmd/k3s/create.go index 57870135..03833775 100644 --- a/cmd/k3s/create.go +++ b/cmd/k3s/create.go @@ -105,7 +105,10 @@ func createK3s(cmd *cobra.Command, _ []string) error { return fmt.Errorf("app availability check failed: %w", err) } - provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) + if err := provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps); err != nil { + progress.Error(err.Error()) + return fmt.Errorf("failed to create management cluster: %w", err) + } return nil } diff --git a/cmd/vultr/create.go b/cmd/vultr/create.go index 5d20ca12..6086ae2b 100644 --- a/cmd/vultr/create.go +++ b/cmd/vultr/create.go @@ -102,7 +102,11 @@ func createVultr(cmd *cobra.Command, _ []string) error { return fmt.Errorf("kubefirst api availability check failed: %w", err) } - provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps) + if err := provision.CreateMgmtCluster(gitAuth, cliFlags, catalogApps); err != nil { + progress.Error(err.Error()) + return fmt.Errorf("failed to create management cluster: %w", err) + } + return nil } diff --git a/internal/cluster/cluster.go b/internal/cluster/cluster.go index 64b5223a..02e2603d 100644 --- a/internal/cluster/cluster.go +++ b/internal/cluster/cluster.go @@ -117,6 +117,8 @@ func ResetClusterProgress(clusterName string) error { return nil } +var ErrNotFound = fmt.Errorf("cluster not found") + func GetCluster(clusterName string) (apiTypes.Cluster, error) { customTransport := http.DefaultTransport.(*http.Transport).Clone() httpClient := http.Client{Transport: customTransport} @@ -137,8 +139,13 @@ func GetCluster(clusterName string) (apiTypes.Cluster, error) { } defer res.Body.Close() - if res.StatusCode != http.StatusOK { - log.Printf("unable to get cluster: %q, continuing", res.Status) + switch res.StatusCode { + case http.StatusNotFound: + return cluster, ErrNotFound + case http.StatusOK: + // continue with the rest + default: + log.Printf("unable to get cluster: %q", res.Status) return cluster, fmt.Errorf("unable to get cluster: %q", res.Status) } diff --git a/internal/provision/provision.go b/internal/provision/provision.go index a97b86e3..4be88948 100644 --- a/internal/provision/provision.go +++ b/internal/provision/provision.go @@ -7,6 +7,7 @@ See the LICENSE file for more details. package provision import ( + "errors" "fmt" apiTypes "github.com/konstructio/kubefirst-api/pkg/types" @@ -25,14 +26,13 @@ func CreateMgmtCluster(gitAuth apiTypes.GitAuth, cliFlags types.CliFlags, catalo ) clusterCreated, err := cluster.GetCluster(clusterRecord.ClusterName) - if err != nil { + 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) } - if !clusterCreated.InProgress { - err = cluster.CreateCluster(clusterRecord) - if err != nil { + if errors.Is(err, cluster.ErrNotFound) { + if err := cluster.CreateCluster(clusterRecord); err != nil { progress.Error(err.Error()) return fmt.Errorf("error creating cluster: %w", err) }