From e0f23051773fc5008391e61cffe8d660479fb4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Tue, 1 Nov 2022 18:04:07 -0300 Subject: [PATCH 1/3] refactor: move local to a single command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/createUtils.go | 4 + cmd/local.go | 2 +- cmd/local/local.go | 700 +++++++++++++++++++++++++++++++ cmd/root.go | 4 + internal/argocd/argocd.go | 34 ++ internal/gitClient/git.go | 9 +- internal/github/github.go | 4 +- internal/githubWrapper/github.go | 8 +- internal/k3d/secrets.go | 14 +- internal/k8s/kubernetes.go | 88 ++++ internal/terraform/terraform.go | 4 +- internal/vault/vault.go | 40 ++ pkg/constants.go | 1 + pkg/helpers.go | 18 +- 14 files changed, 907 insertions(+), 23 deletions(-) create mode 100644 cmd/local/local.go diff --git a/cmd/createUtils.go b/cmd/createUtils.go index f0ad9d4cb..3bdef8016 100644 --- a/cmd/createUtils.go +++ b/cmd/createUtils.go @@ -19,6 +19,7 @@ import ( ) // todo: move it to internals/ArgoCD +// deprecated func setArgocdCreds(dryRun bool) { if dryRun { log.Printf("[#99] Dry-run mode, setArgocdCreds skipped.") @@ -43,6 +44,7 @@ func setArgocdCreds(dryRun bool) { viper.WriteConfig() } +// deprecated func waitArgoCDToBeReady(dryRun bool) { if dryRun { log.Printf("[#99] Dry-run mode, waitArgoCDToBeReady skipped.") @@ -76,6 +78,7 @@ func waitArgoCDToBeReady(dryRun bool) { } } +// deprecated func waitVaultToBeRunning(dryRun bool) { if dryRun { log.Printf("[#99] Dry-run mode, waitVaultToBeRunning skipped.") @@ -115,6 +118,7 @@ func waitVaultToBeRunning(dryRun bool) { } } +// deprecated func loopUntilPodIsReady(dryRun bool) { if dryRun { log.Printf("[#99] Dry-run mode, loopUntilPodIsReady skipped.") diff --git a/cmd/local.go b/cmd/local.go index 27e080ddd..57e3175a5 100644 --- a/cmd/local.go +++ b/cmd/local.go @@ -65,7 +65,7 @@ func init() { //Group Flags - rootCmd.AddCommand(localCmd) + //rootCmd.AddCommand(localCmd) currentCommand := localCmd //log.SetPrefix("LOG: ") //log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) diff --git a/cmd/local/local.go b/cmd/local/local.go new file mode 100644 index 000000000..efe0abc27 --- /dev/null +++ b/cmd/local/local.go @@ -0,0 +1,700 @@ +package local + +import ( + "context" + "errors" + "fmt" + "github.com/go-git/go-git/v5/plumbing" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/argocd" + "github.com/kubefirst/kubefirst/internal/domain" + "github.com/kubefirst/kubefirst/internal/downloadManager" + "github.com/kubefirst/kubefirst/internal/gitClient" + "github.com/kubefirst/kubefirst/internal/githubWrapper" + "github.com/kubefirst/kubefirst/internal/handlers" + "github.com/kubefirst/kubefirst/internal/helm" + "github.com/kubefirst/kubefirst/internal/k3d" + "github.com/kubefirst/kubefirst/internal/k8s" + "github.com/kubefirst/kubefirst/internal/progressPrinter" + "github.com/kubefirst/kubefirst/internal/repo" + "github.com/kubefirst/kubefirst/internal/services" + "github.com/kubefirst/kubefirst/internal/terraform" + "github.com/kubefirst/kubefirst/internal/vault" + "github.com/kubefirst/kubefirst/pkg" + "github.com/segmentio/analytics-go" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "log" + "net/http" + "os" + "os/exec" + "syscall" + "time" +) + +var ( + useTelemetry bool + dryRun bool + silentMode bool + gitHubHost string + gitHubOwner string + gitHubUser string + gitOpsBranch string + gitOpsRepo string + cloud string + clusterName string + awsNodeSpot bool // todo: add + awsAssumeRole string + awsHostedZone string + metaphorBranch string + gitProvider string + adminEmail string +) + +func NewCommand() *cobra.Command { + + localCmd := &cobra.Command{ + Use: "local", + Short: "Kubefirst localhost installation", + Long: "Kubefirst localhost enable a localhost installation without the requirement of a cloud provider.", + RunE: runLocal, + } + + localCmd.Flags().BoolVar(&useTelemetry, "use-telemetry", true, "xinstaller will not send telemetry about this installation") + localCmd.Flags().BoolVar(&dryRun, "dry-run", false, "set to dry-run mode, no changes done on cloud provider selected") + localCmd.Flags().BoolVar(&silentMode, "silent", false, "enable silentMode mode will make the UI return less content to the screen") + localCmd.Flags().StringVar(&gitHubHost, "github-host", "github.com", "Github URL") + localCmd.Flags().StringVar(&gitHubOwner, "github-owner", "", "Github owner of repos") + localCmd.Flags().StringVar(&gitHubUser, "github-user", "", "Github user") + localCmd.Flags().StringVar(&clusterName, "cluster-name", "kubefirst", "the cluster name, used to identify resources on cloud provider") + localCmd.Flags().StringVar(&awsAssumeRole, "aws-assume-role", "", "instead of using AWS IAM user credentials, AWS AssumeRole feature generate role based credentials, more at https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html") + localCmd.Flags().StringVar(&awsHostedZone, "hosted-zone-name", "", "the domain to provision the kubefirst platform in") + localCmd.Flags().StringVar(&gitProvider, "git-provider", "github", "specify \"github\" or \"gitlab\" git provider. defaults to github.") + localCmd.Flags().StringVar(&adminEmail, "admin-email", "", "the email address for the administrator as well as for lets-encrypt certificate emails") + + //initCmd.Flags().BoolVar(&awsNodeSpot, "aws-nodes-spot", false, "nodes spot on AWS EKS compute nodes") + //initCmd.Flags().StringVar("s3-suffix", "", "unique identifier for s3 buckets") + //initCmd.Flags().String("profile", "", "AWS profile located at ~/.aws/config") + //initCmd.Flags().String("region", "", "the region to provision the cloud resources in") + + localCmd.Flags().StringVar(&metaphorBranch, "metaphor-branch", "main", "metaphro application branch") + localCmd.Flags().StringVar(&gitOpsBranch, "gitops-branch", "main", "version/branch used on git clone - former: version-gitops flag") + localCmd.Flags().StringVar(&gitOpsRepo, "gitops-repo", "gitops", "") + //initCmd.Flags().StringP("config", "c", "", "File to be imported to bootstrap configs") + //viper.BindPFlag("config.file", currentCommand.Flags().Lookup("config-load")) + + return localCmd +} + +func runLocal(cmd *cobra.Command, args []string) error { + + config := configs.ReadConfig() + + // todo: viper struct + gitopsRepo, err := cmd.Flags().GetString("gitops-repo") + if err != nil { + return err + } + gitopsOwner, err := cmd.Flags().GetString("gitops-repo") + if err != nil { + return err + } + err = NewInit(gitopsRepo, gitopsOwner, "github", "main") + if err != nil { + log.Println(err) + return err + } + + // telemetry + if useTelemetry { + // Instantiates a SegmentIO client to send messages to the segment API. + segmentIOClientStart := analytics.New(pkg.SegmentIOWriteKey) + + // SegmentIO library works with queue that is based on timing, we explicit close the http client connection + // to force flush in case there is still some pending message in the SegmentIO library queue. + defer func(segmentIOClient analytics.Client) { + err := segmentIOClient.Close() + if err != nil { + log.Println(err) + } + }(segmentIOClientStart) + + telemetryDomainStart, err := domain.NewTelemetry( + pkg.MetricMgmtClusterInstallStarted, + "", + configs.K1Version, + ) + if err != nil { + log.Println(err) + } + telemetryServiceStart := services.NewSegmentIoService(segmentIOClientStart) + telemetryHandlerStart := handlers.NewTelemetryHandler(telemetryServiceStart) + + err = telemetryHandlerStart.SendCountMetric(telemetryDomainStart) + if err != nil { + log.Println(err) + } + } + + // todo need to add go channel to control when ngrok should close + // and use context to handle closing the open goroutine/connection + go pkg.RunNgrok(context.TODO(), pkg.LocalAtlantisURL) + time.Sleep(5 * time.Second) + + if !viper.GetBool("kubefirst.done") { + if viper.GetString("gitprovider") == "github" { + log.Println("Installing Github version of Kubefirst") + viper.Set("git.mode", "github") + // if not local it is AWS for now + // todo: internal + err = k3d.CreateK3dCluster() + if err != nil { + return err + } + } + viper.Set("kubefirst.done", true) + viper.WriteConfig() + } + // start + + //infoCmd need to be before the bars or it is printed in between bars: + //Let's try to not move it on refactors + //infoCmd.Run(cmd, args) + var kPortForwardArgocd *exec.Cmd + progressPrinter.AddTracker("step-0", "Process Parameters", 1) + progressPrinter.AddTracker("step-github", "Setup gitops on github", 3) + progressPrinter.AddTracker("step-base", "Setup base cluster", 2) + progressPrinter.AddTracker("step-apps", "Install apps to cluster", 5) + progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), silentMode) + + progressPrinter.IncrementTracker("step-0", 1) + + if !useTelemetry { + pkg.InformUser("Telemetry Disabled", silentMode) + } + + executionControl := viper.GetBool("terraform.github.apply.complete") + //* create github teams in the org and gitops repo + if !executionControl { + pkg.InformUser("Creating github resources with terraform", silentMode) + + tfEntrypoint := config.GitOpsRepoPath + "/terraform/github" + terraform.InitApplyAutoApprove(dryRun, tfEntrypoint) + + pkg.InformUser(fmt.Sprintf("Created gitops Repo in github.com/%s", viper.GetString("github.owner")), silentMode) + progressPrinter.IncrementTracker("step-github", 1) + } else { + log.Println("already created github terraform resources") + } + + //* push our locally detokenized gitops repo to remote github + githubHost := viper.GetString("github.host") + githubOwner := viper.GetString("github.owner") + localRepo := "gitops" + remoteName := "github" + executionControl = viper.GetBool("github.gitops.hydrated") // todo fix this executionControl value `github.detokenized-gitops.pushed`? + if !executionControl { + pkg.InformUser(fmt.Sprintf("pushing local detokenized gitops content to new remote github.com/%s", viper.GetString("github.owner")), silentMode) + gitClient.PushLocalRepoToEmptyRemote(githubHost, githubOwner, localRepo, remoteName) + } else { + log.Println("already hydrated the github gitops repository") + } + progressPrinter.IncrementTracker("step-github", 1) + + //* create kubernetes cluster + executionControl = viper.GetBool("k3d.created") + if !executionControl { + pkg.InformUser("Creating K8S Cluster", silentMode) + err = k3d.CreateK3dCluster() + if err != nil { + log.Println("Error installing k3d cluster") + return err + } + progressPrinter.IncrementTracker("step-base", 1) + } else { + log.Println("already created k3d cluster") + } + progressPrinter.IncrementTracker("step-github", 1) + + // add secrets to cluster + // todo there is a secret condition in AddK3DSecrets to this not checked + executionControl = viper.GetBool("kubernetes.vault.secret.created") + if !executionControl { + err = k3d.AddK3DSecrets(dryRun) + if err != nil { + log.Println("Error AddK3DSecrets") + return err + } + } else { + log.Println("already added secrets to k3d cluster") + } + + // create argocd initial repository config + executionControl = viper.GetBool("argocd.initial-repository.created") + if !executionControl { + pkg.InformUser("create initial argocd repository", silentMode) + //Enterprise users need to be able to set the hostname for git. + gitopsRepo := fmt.Sprintf("git@%s:%s/gitops.git", viper.GetString("github.host"), viper.GetString("github.owner")) + err = argocd.CreateInitialArgoCDRepository(gitopsRepo) + if err != nil { + log.Println("Error CreateInitialArgoCDRepository") + return err + } + } else { + log.Println("already created initial argocd repository") + } + + //* helm add argo repository && update + helmRepo := helm.HelmRepo{} + helmRepo.RepoName = "argo" + helmRepo.RepoURL = "https://argoproj.github.io/argo-helm" + helmRepo.ChartName = "argo-cd" + helmRepo.Namespace = "argocd" + helmRepo.ChartVersion = "4.10.5" + + executionControl = viper.GetBool("argocd.helm.repo.updated") + if !executionControl { + pkg.InformUser(fmt.Sprintf("helm repo add %s %s and helm repo update", helmRepo.RepoName, helmRepo.RepoURL), silentMode) + helm.AddRepoAndUpdateRepo(dryRun, helmRepo) + } + + //* helm install argocd + executionControl = viper.GetBool("argocd.helm.install.complete") + if !executionControl { + pkg.InformUser(fmt.Sprintf("helm install %s and wait", helmRepo.RepoName), silentMode) + helm.Install(dryRun, helmRepo) + } + progressPrinter.IncrementTracker("step-apps", 1) + + //* argocd pods are running + executionControl = viper.GetBool("argocd.ready") + if !executionControl { + argocd.WaitArgoCDToBeReady(dryRun) + pkg.InformUser("ArgoCD is running, continuing", silentMode) + } else { + log.Println("already waited for argocd to be ready") + } + + //* establish port-forward + kPortForwardArgocd, err = k8s.PortForward(dryRun, "argocd", "svc/argocd-server", "8080:80") + defer func() { + err = kPortForwardArgocd.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Println("Error closing kPortForwardArgocd") + } + }() + pkg.InformUser(fmt.Sprintf("port-forward to argocd is available at %s", viper.GetString("argocd.local.service")), silentMode) + + //* argocd pods are ready, get and set credentials + executionControl = viper.GetBool("argocd.credentials.set") + if !executionControl { + pkg.InformUser("Setting argocd username and password credentials", silentMode) + k8s.SetArgocdCreds(dryRun) + pkg.InformUser("argocd username and password credentials set successfully", silentMode) + + pkg.InformUser("Getting an argocd auth token", silentMode) + _ = argocd.GetArgocdAuthToken(dryRun) + pkg.InformUser("argocd admin auth token set", silentMode) + + viper.Set("argocd.credentials.set", true) + viper.WriteConfig() + } + + //* argocd sync registry and start sync waves + executionControl = viper.GetBool("argocd.registry.applied") + if !executionControl { + pkg.InformUser("applying the registry application to argocd", silentMode) + err = argocd.ApplyRegistryLocal(dryRun) + if err != nil { + log.Println("Error applying registry application to argocd") + return err + } + } + + progressPrinter.IncrementTracker("step-apps", 1) + + //* vault in running state + executionControl = viper.GetBool("vault.status.running") + if !executionControl { + pkg.InformUser("Waiting for vault to be ready", silentMode) + vault.WaitVaultToBeRunning(dryRun) + if err != nil { + log.Println("error waiting for vault to become running") + return err + } + } + kPortForwardVault, err := k8s.PortForward(dryRun, "vault", "svc/vault", "8200:8200") + defer func() { + err = kPortForwardVault.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Println("Error closing kPortForwardVault") + } + }() + + k8s.LoopUntilPodIsReady(dryRun) + kPortForwardMinio, err := k8s.PortForward(dryRun, "minio", "svc/minio", "9000:9000") + defer func() { + err = kPortForwardMinio.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Println("Error closing kPortForwardMinio") + } + }() + + //* configure vault with terraform + executionControl = viper.GetBool("terraform.vault.apply.complete") + if !executionControl { + // todo evaluate progressPrinter.IncrementTracker("step-vault", 1) + //* set known vault token + viper.Set("vault.token", "k1_local_vault_token") + viper.WriteConfig() + + //* run vault terraform + pkg.InformUser("configuring vault with terraform", silentMode) + tfEntrypoint := config.GitOpsRepoPath + "/terraform/vault" + terraform.InitApplyAutoApprove(dryRun, tfEntrypoint) + + pkg.InformUser("vault terraform executed successfully", silentMode) + + //* create vault configurerd secret + // todo remove this code + log.Println("creating vault configured secret") + k8s.CreateVaultConfiguredSecret(dryRun, config) + pkg.InformUser("Vault secret created", silentMode) + } else { + log.Println("already executed vault terraform") + } + + //* create users + executionControl = viper.GetBool("terraform.users.apply.complete") + if !executionControl { + pkg.InformUser("applying users terraform", silentMode) + + tfEntrypoint := config.GitOpsRepoPath + "/terraform/users" + terraform.InitApplyAutoApprove(dryRun, tfEntrypoint) + + pkg.InformUser("executed users terraform successfully", silentMode) + // progressPrinter.IncrementTracker("step-users", 1) + } else { + log.Println("already created users with terraform") + } + + // TODO: K3D => NEED TO REMOVE local-backend.tf and rename remote-backend.md + + pkg.InformUser("Welcome to local kubefirst experience", silentMode) + pkg.InformUser("To use your cluster port-forward - argocd", silentMode) + pkg.InformUser("If not automatically injected, your kubeconfig is at:", silentMode) + pkg.InformUser("k3d kubeconfig get "+viper.GetString("cluster-name"), silentMode) + pkg.InformUser("Expose Argo-CD", silentMode) + pkg.InformUser("kubectl -n argocd port-forward svc/argocd-server 8080:80", silentMode) + pkg.InformUser("Argo User: "+viper.GetString("argocd.admin.username"), silentMode) + pkg.InformUser("Argo Password: "+viper.GetString("argocd.admin.password"), silentMode) + time.Sleep(1 * time.Second) + progressPrinter.IncrementTracker("step-apps", 1) + progressPrinter.IncrementTracker("step-base", 1) + progressPrinter.IncrementTracker("step-apps", 1) + + // end + + if !viper.GetBool("chartmuseum.host.resolved") { + + //* establish port-forward + var kPortForwardChartMuseum *exec.Cmd + kPortForwardChartMuseum, err = k8s.PortForward(dryRun, "chartmuseum", "svc/chartmuseum", "8181:8080") + defer func() { + err = kPortForwardChartMuseum.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Println("Error closing kPortForwardChartMuseum") + } + }() + pkg.AwaitHostNTimes("http://localhost:8181/health", 5, 5) + viper.Set("chartmuseum.host.resolved", true) + viper.WriteConfig() + } else { + log.Println("already resolved host for chartmuseum, continuing") + } + + // todo: uncomment it + //pkg.InformUser("Deploying metaphor applications", silentMode) + //err = deployMetaphorCmd.RunE(cmd, args) + //if err != nil { + // pkg.InformUser("Error deploy metaphor applications", silentMode) + // log.Println("Error running deployMetaphorCmd") + // return err + //} + + //kPortForwardAtlantis, err := k8s.PortForward(dryRun, "atlantis", "svc/atlantis", "4141:80") + //defer func() { + // err = kPortForwardAtlantis.Process.Signal(syscall.SIGTERM) + // if err != nil { + // log.Println("error closing kPortForwardAtlantis") + // } + //}() + + // --- + + // update terraform s3 backend to internal k8s dns (s3/minio bucket) + err = pkg.ReplaceS3Backend() + if err != nil { + return err + } + + // create a new branch and push changes + githubHost = viper.GetString("github.host") + githubOwner = viper.GetString("github.owner") + remoteName = "github" + localRepo = "gitops" + branchName := "update-s3-backend" + branchNameRef := plumbing.ReferenceName("refs/heads/" + branchName) + + gitClient.UpdateLocalTFFilesAndPush( + githubHost, + githubOwner, + localRepo, + remoteName, + branchNameRef, + ) + + fmt.Println("sleeping after commit...") + time.Sleep(3 * time.Second) + + // create a PR, atlantis will identify it's a terraform change/file update and, + // trigger atlantis plan + g := githubWrapper.New() + err = g.CreatePR(branchName) + if err != nil { + fmt.Println(err) + } + log.Println("sleeping after create PR...") + time.Sleep(5 * time.Second) + log.Println("sleeping... atlantis plan should be running") + time.Sleep(5 * time.Second) + + fmt.Println("sleeping before apply...") + time.Sleep(120 * time.Second) + + // after 120 seconds, it will comment in the PR with atlantis plan + err = g.CommentPR(1, "atlantis apply") + if err != nil { + fmt.Println(err) + } + + log.Println("sending mgmt cluster install completed metric") + + if useTelemetry { + // Instantiates a SegmentIO client to send messages to the segment API. + segmentIOClientCompleted := analytics.New(pkg.SegmentIOWriteKey) + + // SegmentIO library works with queue that is based on timing, we explicit close the http client connection + // to force flush in case there is still some pending message in the SegmentIO library queue. + defer func(segmentIOClientCompleted analytics.Client) { + err := segmentIOClientCompleted.Close() + if err != nil { + log.Println(err) + } + }(segmentIOClientCompleted) + + telemetryDomainCompleted, err := domain.NewTelemetry( + pkg.MetricMgmtClusterInstallCompleted, + "", + configs.K1Version, + ) + if err != nil { + log.Println(err) + } + telemetryServiceCompleted := services.NewSegmentIoService(segmentIOClientCompleted) + telemetryHandlerCompleted := handlers.NewTelemetryHandler(telemetryServiceCompleted) + + err = telemetryHandlerCompleted.SendCountMetric(telemetryDomainCompleted) + if err != nil { + log.Println(err) + } + } + + log.Println("Kubefirst installation finished successfully") + pkg.InformUser("Kubefirst installation finished successfully", silentMode) + + // todo: temporary code to enable console for localhost / enable it back! + //err = postInstallCmd.RunE(cmd, args) + //if err != nil { + // pkg.InformUser("Error starting apps from post-install", silentMode) + // log.Println("Error running postInstallCmd") + // return err + //} + + return nil + +} + +func NewInit(gitopsRepo string, gitopsOwner string, gitProvider string, metaphorBranch string) error { + + //tools.RunInfo(cmd, args) + // todo: load it from viper + config := configs.ReadConfig() + + // set default values + viper.Set("gitops.repo", gitOpsRepo) + // todo: do we need it? + viper.Set("gitops.owner", "kubefirst") + + viper.Set("gitprovider", gitProvider) + viper.Set("metaphor.branch", metaphorBranch) + viper.WriteConfig() + // + if err := pkg.ValidateK1Folder(config.K1FolderPath); err != nil { + return err + } + + // todo: wrap business logic into the handler + if config.GitHubPersonalAccessToken == "" { + + httpClient := http.DefaultClient + gitHubService := services.NewGitHubService(httpClient) + gitHubHandler := handlers.NewGitHubHandler(gitHubService) + gitHubAccessToken, err := gitHubHandler.AuthenticateUser() + if err != nil { + return err + } + + if len(gitHubAccessToken) == 0 { + return errors.New("unable to retrieve a GitHub token for the user") + } + + viper.Set("github.token", gitHubAccessToken) + err = viper.WriteConfig() + if err != nil { + return err + } + + // todo: set common way to load env. values (viper->struct->load-env) + // todo: use viper file to load it, not load env. value + if err := os.Setenv("GITHUB_AUTH_TOKEN", gitHubAccessToken); err != nil { + return err + } + log.Println("\nGITHUB_AUTH_TOKEN set via OAuth") + } + + // review it + viper.Set("gitops.branch", gitOpsBranch) + viper.Set("github.owner", viper.GetString("github.user")) + viper.Set("cloud", pkg.CloudK3d) + viper.Set("cluster-name", clusterName) + viper.Set("adminemail", adminEmail) + viper.WriteConfig() + + if silentMode { + pkg.InformUser( + "Silent mode enabled, most of the UI prints wont be showed. Please check the logs for more details.\n", + silentMode, + ) + } + + progressPrinter.AddTracker("step-download", pkg.DownloadDependencies, 3) + progressPrinter.AddTracker("step-gitops", pkg.CloneAndDetokenizeGitOpsTemplate, 1) + progressPrinter.AddTracker("step-ssh", pkg.CreateSSHKey, 1) + progressPrinter.AddTracker("step-telemetry", pkg.SendTelemetry, 1) + + progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), silentMode) + + log.Println("sending init started metric") + + // todo: + var telemetryHandler handlers.TelemetryHandler + if useTelemetry { + + // Instantiates a SegmentIO client to use send messages to the segment API. + segmentIOClient := analytics.New(pkg.SegmentIOWriteKey) + + // SegmentIO library works with queue that is based on timing, we explicit close the http client connection + // to force flush in case there is still some pending message in the SegmentIO library queue. + defer func(segmentIOClient analytics.Client) { + err := segmentIOClient.Close() + if err != nil { + log.Println(err) + } + }(segmentIOClient) + + // validate telemetryDomain data + telemetryDomain, err := domain.NewTelemetry( + pkg.MetricInitStarted, + awsHostedZone, + configs.K1Version, + ) + if err != nil { + log.Println(err) + } + telemetryService := services.NewSegmentIoService(segmentIOClient) + telemetryHandler = handlers.NewTelemetryHandler(telemetryService) + + err = telemetryHandler.SendCountMetric(telemetryDomain) + if err != nil { + log.Println(err) + } + } + + // todo: set constants + viper.Set("argocd.local.service", "http://localhost:8080") + viper.Set("gitlab.local.service", "http://localhost:8888") + viper.Set("vault.local.service", "http://localhost:8200") + // used for letsencrypt notifications and the gitlab root account + + atlantisWebhookSecret := pkg.Random(20) + viper.Set("github.atlantis.webhook.secret", atlantisWebhookSecret) + + viper.WriteConfig() + + //! tracker 0 + log.Println("installing kubefirst dependencies") + progressPrinter.IncrementTracker("step-download", 1) + err := downloadManager.DownloadTools(config) + if err != nil { + return err + } + log.Println("dependency installation complete") + progressPrinter.IncrementTracker("step-download", 1) + err = downloadManager.DownloadLocalTools(config) + if err != nil { + return err + } + + //Fix incomplete bar, please don't remove it. + progressPrinter.IncrementTracker("step-download", 1) + + //! tracker 5 + log.Println("creating an ssh key pair for your new cloud infrastructure") + pkg.CreateSshKeyPair() + log.Println("ssh key pair creation complete") + progressPrinter.IncrementTracker("step-ssh", 1) + + //! tracker 6 + + repo.PrepareKubefirstTemplateRepo(dryRun, config, viper.GetString("github.owner"), viper.GetString("gitops.repo"), viper.GetString("gitops.branch"), viper.GetString("template.tag")) + log.Println("clone and detokenization of gitops-template repository complete") + progressPrinter.IncrementTracker("step-gitops", 1) + + log.Println("sending init completed metric") + + if useTelemetry { + telemetryInitCompleted, err := domain.NewTelemetry( + pkg.MetricInitCompleted, + awsHostedZone, + configs.K1Version, + ) + if err != nil { + log.Println(err) + } + err = telemetryHandler.SendCountMetric(telemetryInitCompleted) + if err != nil { + log.Println(err) + } + } + + viper.WriteConfig() + + //! tracker 8 + progressPrinter.IncrementTracker("step-telemetry", 1) + time.Sleep(time.Millisecond * 100) + + pkg.InformUser("init is done!\n", silentMode) + + return nil +} diff --git a/cmd/root.go b/cmd/root.go index 88c3d5976..ff8b74736 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "github.com/kubefirst/kubefirst/cmd/local" "os" "github.com/kubefirst/kubefirst/internal/progressPrinter" @@ -41,4 +42,7 @@ func init() { // Cobra also supports local flags, which will only run, when this action is called directly. rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + // todo: temporary, move it into CLI tree + rootCmd.AddCommand(local.NewCommand()) + } diff --git a/internal/argocd/argocd.go b/internal/argocd/argocd.go index 4e0628324..d2984cfbb 100644 --- a/internal/argocd/argocd.go +++ b/internal/argocd/argocd.go @@ -402,3 +402,37 @@ func IsAppSynched(token string, applicationName string) (bool, error) { } return false, nil } + +// todo: document it, deprecate the other waitArgoCDToBeReady +func WaitArgoCDToBeReady(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, waitArgoCDToBeReady skipped.") + return + } + config := configs.ReadConfig() + x := 50 + for i := 0; i < x; i++ { + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/argocd") + if err != nil { + log.Println("Waiting argocd to be born") + time.Sleep(10 * time.Second) + } else { + log.Println("argocd namespace found, continuing") + time.Sleep(5 * time.Second) + break + } + } + for i := 0; i < x; i++ { + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "get", "pods", "-l", "app.kubernetes.io/name=argocd-server") + if err != nil { + log.Println("Waiting for argocd pods to create, checking in 10 seconds") + time.Sleep(10 * time.Second) + } else { + log.Println("argocd pods found, waiting for them to be running") + viper.Set("argocd.ready", true) + viper.WriteConfig() + time.Sleep(15 * time.Second) + break + } + } +} diff --git a/internal/gitClient/git.go b/internal/gitClient/git.go index b21677012..7305f0596 100644 --- a/internal/gitClient/git.go +++ b/internal/gitClient/git.go @@ -61,7 +61,7 @@ func PopulateRepoWithToken(owner string, repo string, sourceFolder string, gitHo //Push config := configs.ReadConfig() - token := os.Getenv("GITHUB_AUTH_TOKEN") + token := viper.GetString("github.token") if token == "" { log.Println("Unauthorized: No token present") return fmt.Errorf("missing github token") @@ -212,6 +212,7 @@ func PushGitopsToSoftServe() { func CloneTemplateRepoWithFallBack(githubOrg string, repoName string, directory string, branch string, fallbackTag string) error { defer viper.WriteConfig() // todo need to refactor this and have the repoName include -template + githubOrg = "kubefirst" repoURL := fmt.Sprintf("https://github.com/%s/%s-template", githubOrg, repoName) isMainBranch := true @@ -326,7 +327,7 @@ func PushLocalRepoToEmptyRemote(githubHost, githubOwner, localRepo, remoteName s }, }) - token := os.Getenv("GITHUB_AUTH_TOKEN") + token := viper.GetString("github.token") if len(token) == 0 { token = viper.GetString("github.token") } @@ -386,7 +387,7 @@ func PushLocalRepoUpdates(githubHost, githubOwner, localRepo, remoteName string) }, }) - token := os.Getenv("GITHUB_AUTH_TOKEN") + token := viper.GetString("github.token") err = repo.Push(&git.PushOptions{ RemoteName: remoteName, Auth: &http.BasicAuth{ @@ -474,7 +475,7 @@ func UpdateLocalTFFilesAndPush(githubHost, githubOwner, localRepo, remoteName st fmt.Println(err) } - token := os.Getenv("GITHUB_AUTH_TOKEN") + token := viper.GetString("github.token") err = repo.Push(&git.PushOptions{ RemoteName: remoteName, Auth: &http.BasicAuth{ diff --git a/internal/github/github.go b/internal/github/github.go index 56d8610b7..2aa49a976 100644 --- a/internal/github/github.go +++ b/internal/github/github.go @@ -26,7 +26,7 @@ func ApplyGitHubTerraform(dryRun bool) { envs["AWS_SDK_LOAD_CONFIG"] = "1" aws.ProfileInjection(&envs) // Prepare for terraform gitlab execution - envs["GITHUB_TOKEN"] = os.Getenv("GITHUB_AUTH_TOKEN") + envs["GITHUB_TOKEN"] = viper.GetString("github.token") envs["GITHUB_OWNER"] = viper.GetString("github.owner") envs["TF_VAR_atlantis_repo_webhook_secret"] = viper.GetString("github.atlantis.webhook.secret") envs["TF_VAR_kubefirst_bot_ssh_public_key"] = viper.GetString("botPublicKey") @@ -66,7 +66,7 @@ func DestroyGitHubTerraform(dryRun bool) { envs["AWS_SDK_LOAD_CONFIG"] = "1" aws.ProfileInjection(&envs) // Prepare for terraform gitlab execution - envs["GITHUB_TOKEN"] = os.Getenv("GITHUB_AUTH_TOKEN") + envs["GITHUB_TOKEN"] = viper.GetString("github.token") envs["GITHUB_OWNER"] = viper.GetString("github.owner") envs["TF_VAR_atlantis_repo_webhook_secret"] = viper.GetString("github.atlantis.webhook.secret") envs["TF_VAR_kubefirst_bot_ssh_public_key"] = viper.GetString("botPublicKey") diff --git a/internal/githubWrapper/github.go b/internal/githubWrapper/github.go index a76341d41..031bf4673 100644 --- a/internal/githubWrapper/github.go +++ b/internal/githubWrapper/github.go @@ -3,13 +3,11 @@ package githubWrapper import ( "context" "fmt" + "github.com/google/go-github/v45/github" "github.com/spf13/viper" + "golang.org/x/oauth2" "log" "net/http" - "os" - - "github.com/google/go-github/v45/github" - "golang.org/x/oauth2" ) type GithubSession struct { @@ -21,7 +19,7 @@ type GithubSession struct { // New - Create a new client for github wrapper func New() GithubSession { - token := os.Getenv("GITHUB_AUTH_TOKEN") + token := viper.GetString("github.token") if token == "" { log.Fatal("Unauthorized: No token present") } diff --git a/internal/k3d/secrets.go b/internal/k3d/secrets.go index 20a15e3c4..5a48f3078 100644 --- a/internal/k3d/secrets.go +++ b/internal/k3d/secrets.go @@ -5,13 +5,11 @@ import ( "encoding/base64" "errors" "fmt" - "log" - "os" - "github.com/kubefirst/kubefirst/internal/k8s" "github.com/spf13/viper" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "log" ) func AddK3DSecrets(dryrun bool) error { @@ -49,9 +47,9 @@ func AddK3DSecrets(dryrun bool) error { "BASIC_AUTH_USER": []byte("k-ray"), "BASIC_AUTH_PASS": []byte("feedkraystars"), "USERNAME": []byte(viper.GetString("github.user")), - "PERSONAL_ACCESS_TOKEN": []byte(os.Getenv("GITHUB_AUTH_TOKEN")), + "PERSONAL_ACCESS_TOKEN": []byte(viper.GetString("github.token")), "username": []byte(viper.GetString("github.user")), - "password": []byte(os.Getenv("GITHUB_AUTH_TOKEN")), + "password": []byte(viper.GetString("github.token")), } argoCiSecrets := &v1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: "ci-secrets", Namespace: "argo"}, @@ -65,7 +63,7 @@ func AddK3DSecrets(dryrun bool) error { viper.Set("kubernetes.argo-ci.secret.created", true) viper.WriteConfig() - usernamePasswordString := fmt.Sprintf("%s:%s", viper.GetString("github.user"), os.Getenv("GITHUB_AUTH_TOKEN")) + usernamePasswordString := fmt.Sprintf("%s:%s", viper.GetString("github.user"), viper.GetString("github.token")) usernamePasswordStringB64 := base64.StdEncoding.EncodeToString([]byte(usernamePasswordString)) dockerConfigString := fmt.Sprintf(`{"auths": {"https://ghcr.io/": {"auth": "%s"}}}`, usernamePasswordStringB64) @@ -82,7 +80,7 @@ func AddK3DSecrets(dryrun bool) error { viper.WriteConfig() dataArgoCd := map[string][]byte{ - "password": []byte(os.Getenv("GITHUB_AUTH_TOKEN")), + "password": []byte(viper.GetString("github.token")), "url": []byte(fmt.Sprintf("https://%s/%s/gitops.git", viper.GetString("github.host"), viper.GetString("github.owner"))), "username": []byte(viper.GetString("github.user")), } @@ -155,7 +153,7 @@ func AddK3DSecrets(dryrun bool) error { viper.WriteConfig() dataGh := map[string][]byte{ - "github_token": []byte(os.Getenv("GITHUB_AUTH_TOKEN")), + "github_token": []byte(viper.GetString("github.token")), } ghRunnerSecret := &v1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: "controller-manager", Namespace: "github-runner"}, diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index 2051442a9..e6e178fb9 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -5,7 +5,9 @@ import ( "context" "encoding/json" "fmt" + "io" "log" + "net/http" "os" "os/exec" "time" @@ -366,3 +368,89 @@ func (p *secret) patchSecret(k8sClient *kubernetes.Clientset, payload []PatchJso } return nil } + +// todo: deprecate the other functions +func LoopUntilPodIsReady(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, loopUntilPodIsReady skipped.") + return + } + token := viper.GetString("vault.token") + if len(token) == 0 { + + totalAttempts := 50 + url := "http://localhost:8200/v1/sys/health" + for i := 0; i < totalAttempts; i++ { + log.Printf("vault is not ready yet, sleeping and checking again, attempt (%d/%d)", i+1, totalAttempts) + time.Sleep(10 * time.Second) + + req, _ := http.NewRequest("GET", url, nil) + + req.Header.Add("Content-Type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + log.Println("error with http request Do, vault is not available", err) + // todo: temporary code + log.Println("trying to open port-forward again...") + go func() { + _, err := PortForward(false, "vault", "svc/vault", "8200:8200") + if err != nil { + log.Println("error opening Vault port forward") + } + }() + continue + } + + defer res.Body.Close() + body, err := io.ReadAll(res.Body) + if err != nil { + log.Println("vault is available but the body is not what is expected ", err) + continue + } + + var responseJson map[string]interface{} + + if err := json.Unmarshal(body, &responseJson); err != nil { + log.Printf("vault is available but the body is not what is expected %s", err) + continue + } + + _, ok := responseJson["initialized"] + if ok { + log.Printf("vault is initialized and is in the expected state") + return + } + log.Panic("vault was never initialized") + } + viper.Set("vault.status.running", true) + viper.WriteConfig() + } else { + log.Println("vault token already exists, skipping vault health checks loopUntilPodIsReady") + } +} + +// todo: deprecate the other functions +func SetArgocdCreds(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, setArgocdCreds skipped.") + viper.Set("argocd.admin.password", "dry-run-not-real-pwd") + viper.Set("argocd.admin.username", "dry-run-not-admin") + viper.WriteConfig() + return + } + clientset, err := GetClientSet(dryRun) + if err != nil { + panic(err.Error()) + } + argocd.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") + + argocdPassword := GetSecretValue(argocd.ArgocdSecretClient, "argocd-initial-admin-secret", "password") + if argocdPassword == "" { + log.Panicf("Missing argocdPassword") + } + + viper.Set("argocd.admin.password", argocdPassword) + viper.Set("argocd.admin.username", "admin") + viper.WriteConfig() +} diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index b3d0297ae..2e9937519 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -97,7 +97,7 @@ func terraformConfig(terraformEntryPoint string) map[string]string { case "users": envs["VAULT_TOKEN"] = viper.GetString("vault.token") envs["VAULT_ADDR"] = viper.GetString("vault.local.service") - envs["GITHUB_TOKEN"] = os.Getenv("GITHUB_AUTH_TOKEN") + envs["GITHUB_TOKEN"] = viper.GetString("github.token") envs["GITHUB_OWNER"] = viper.GetString("github.owner") return envs } @@ -358,7 +358,7 @@ func ApplyUsersTerraform(dryRun bool, directory string, gitProvider string) erro envs := map[string]string{} if gitProvider == "github" { - envs["GITHUB_TOKEN"] = os.Getenv("GITHUB_AUTH_TOKEN") + envs["GITHUB_TOKEN"] = viper.GetString("github.token") envs["GITHUB_OWNER"] = viper.GetString("github.owner") } else if gitProvider == "gitlab" { envs["GITLAB_TOKEN"] = viper.GetString("gitlab.token") diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 9ef776091..282808d6f 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "syscall" + "time" vault "github.com/hashicorp/vault/api" "github.com/kubefirst/kubefirst/configs" @@ -196,3 +197,42 @@ func GetOidcClientCredentials(dryRun bool) { viper.WriteConfig() } + +func WaitVaultToBeRunning(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, waitVaultToBeRunning skipped.") + return + } + token := viper.GetString("vault.token") + if len(token) == 0 { + config := configs.ReadConfig() + x := 50 + for i := 0; i < x; i++ { + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/vault") + if err != nil { + log.Println("Waiting vault to be born") + time.Sleep(10 * time.Second) + } else { + log.Println("vault namespace found, continuing") + time.Sleep(25 * time.Second) + break + } + } + + //! failing + x = 50 + for i := 0; i < x; i++ { + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "get", "pods", "-l", "app.kubernetes.io/instance=vault") + if err != nil { + log.Println("Waiting vault pods to create") + time.Sleep(10 * time.Second) + } else { + log.Println("vault pods found, continuing") + time.Sleep(15 * time.Second) + break + } + } + } else { + log.Println("vault token arleady exists, skipping vault health checks waitVaultToBeRunning") + } +} diff --git a/pkg/constants.go b/pkg/constants.go index 0ea8870fd..e76b2fc35 100644 --- a/pkg/constants.go +++ b/pkg/constants.go @@ -8,6 +8,7 @@ const ( LocalConsoleUI = "http://localhost:9094" GitHubOAuthClientId = "2ced340927e0a6c49a45" CloudK3d = "k3d" + GitHubProviderName = "github" ) // SegmentIO constants diff --git a/pkg/helpers.go b/pkg/helpers.go index cf0952ab9..8810c548b 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -3,6 +3,7 @@ package pkg import ( "errors" "fmt" + "github.com/kubefirst/kubefirst/internal/progressPrinter" "log" "math/rand" "net/http" @@ -133,7 +134,7 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { githubUser := viper.GetString(("github.user")) //TODO: We need to fix this - githubToken := os.Getenv("GITHUB_AUTH_TOKEN") + githubToken := viper.GetString("github.token") //todo: get from viper gitopsRepo := "gitops" @@ -478,3 +479,18 @@ func ReplaceS3Backend() error { return nil } + +// todo: deprecate cmd.informUser +func InformUser(message string, silentMode bool) { + // if in silent mode, send message to the screen + // silent mode will silent most of the messages, this function is not frequently called + if silentMode { + _, err := fmt.Fprintln(os.Stdout, message) + if err != nil { + log.Println(err) + } + return + } + log.Println(message) + progressPrinter.LogMessage(fmt.Sprintf("- %s", message)) +} From fb9c9a001913cebcd53c172f8793b7998892baa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Tue, 1 Nov 2022 22:29:02 -0300 Subject: [PATCH 2/3] chore: wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 4 +- cmd/local/local.go | 341 +++++++++++++++++--------------------- internal/gitClient/git.go | 29 +--- pkg/constants.go | 9 + pkg/helpers.go | 2 +- 5 files changed, 174 insertions(+), 211 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 28fee6963..697925852 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -267,7 +267,7 @@ cluster provisioning process spinning up the services, and validates the livenes // todo: wire it up in the architecture / files / folder // update terraform s3 backend to internal k8s dns (s3/minio bucket) - err = pkg.ReplaceS3Backend() + err = pkg.ReplaceTerraformS3Backend() if err != nil { return err } @@ -280,7 +280,7 @@ cluster provisioning process spinning up the services, and validates the livenes branchName := "update-s3-backend" branchNameRef := plumbing.ReferenceName("refs/heads/" + branchName) - gitClient.UpdateLocalTFFilesAndPush( + gitClient.UpdateLocalTerraformFilesAndPush( githubHost, githubOwner, localRepo, diff --git a/cmd/local/local.go b/cmd/local/local.go index efe0abc27..7b78efdcb 100644 --- a/cmd/local/local.go +++ b/cmd/local/local.go @@ -28,6 +28,7 @@ import ( "net/http" "os" "os/exec" + "sync" "syscall" "time" ) @@ -36,47 +37,29 @@ var ( useTelemetry bool dryRun bool silentMode bool - gitHubHost string - gitHubOwner string - gitHubUser string gitOpsBranch string gitOpsRepo string - cloud string - clusterName string - awsNodeSpot bool // todo: add - awsAssumeRole string awsHostedZone string metaphorBranch string - gitProvider string adminEmail string ) func NewCommand() *cobra.Command { localCmd := &cobra.Command{ - Use: "local", - Short: "Kubefirst localhost installation", - Long: "Kubefirst localhost enable a localhost installation without the requirement of a cloud provider.", - RunE: runLocal, + Use: "local", + Short: "Kubefirst localhost installation", + Long: "Kubefirst localhost enable a localhost installation without the requirement of a cloud provider.", + PreRunE: validateLocal, + RunE: runLocal, } localCmd.Flags().BoolVar(&useTelemetry, "use-telemetry", true, "xinstaller will not send telemetry about this installation") localCmd.Flags().BoolVar(&dryRun, "dry-run", false, "set to dry-run mode, no changes done on cloud provider selected") localCmd.Flags().BoolVar(&silentMode, "silent", false, "enable silentMode mode will make the UI return less content to the screen") - localCmd.Flags().StringVar(&gitHubHost, "github-host", "github.com", "Github URL") - localCmd.Flags().StringVar(&gitHubOwner, "github-owner", "", "Github owner of repos") - localCmd.Flags().StringVar(&gitHubUser, "github-user", "", "Github user") - localCmd.Flags().StringVar(&clusterName, "cluster-name", "kubefirst", "the cluster name, used to identify resources on cloud provider") - localCmd.Flags().StringVar(&awsAssumeRole, "aws-assume-role", "", "instead of using AWS IAM user credentials, AWS AssumeRole feature generate role based credentials, more at https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html") - localCmd.Flags().StringVar(&awsHostedZone, "hosted-zone-name", "", "the domain to provision the kubefirst platform in") - localCmd.Flags().StringVar(&gitProvider, "git-provider", "github", "specify \"github\" or \"gitlab\" git provider. defaults to github.") + // todo: get it from GH token localCmd.Flags().StringVar(&adminEmail, "admin-email", "", "the email address for the administrator as well as for lets-encrypt certificate emails") - //initCmd.Flags().BoolVar(&awsNodeSpot, "aws-nodes-spot", false, "nodes spot on AWS EKS compute nodes") - //initCmd.Flags().StringVar("s3-suffix", "", "unique identifier for s3 buckets") - //initCmd.Flags().String("profile", "", "AWS profile located at ~/.aws/config") - //initCmd.Flags().String("region", "", "the region to provision the cloud resources in") - localCmd.Flags().StringVar(&metaphorBranch, "metaphor-branch", "main", "metaphro application branch") localCmd.Flags().StringVar(&gitOpsBranch, "gitops-branch", "main", "version/branch used on git clone - former: version-gitops flag") localCmd.Flags().StringVar(&gitOpsRepo, "gitops-repo", "gitops", "") @@ -90,21 +73,62 @@ func runLocal(cmd *cobra.Command, args []string) error { config := configs.ReadConfig() - // todo: viper struct - gitopsRepo, err := cmd.Flags().GetString("gitops-repo") - if err != nil { - return err + //tools.RunInfo(cmd, args) + + progressPrinter.AddTracker("step-download", pkg.DownloadDependencies, 3) + progressPrinter.AddTracker("step-gitops", pkg.CloneAndDetokenizeGitOpsTemplate, 1) + progressPrinter.AddTracker("step-ssh", pkg.CreateSSHKey, 1) + progressPrinter.AddTracker("step-0", "Process Parameters", 1) + progressPrinter.AddTracker("step-github", "Setup gitops on github", 3) + progressPrinter.AddTracker("step-base", "Setup base cluster", 2) + progressPrinter.AddTracker("step-apps", "Install apps to cluster", 5) + + progressPrinter.IncrementTracker("step-base", 1) + progressPrinter.IncrementTracker("step-base", 1) + + if useTelemetry { + progressPrinter.AddTracker("step-telemetry", pkg.SendTelemetry, 1) } - gitopsOwner, err := cmd.Flags().GetString("gitops-repo") + + progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), silentMode) + + log.Println("sending init started metric") + + log.Println("installing kubefirst dependencies") + progressPrinter.IncrementTracker("step-download", 1) + err := downloadManager.DownloadTools(config) if err != nil { return err } - err = NewInit(gitopsRepo, gitopsOwner, "github", "main") + log.Println("dependency installation complete") + progressPrinter.IncrementTracker("step-download", 1) + err = downloadManager.DownloadLocalTools(config) if err != nil { - log.Println(err) return err } + progressPrinter.IncrementTracker("step-download", 1) + + log.Println("creating an ssh key pair for your new cloud infrastructure") + pkg.CreateSshKeyPair() + log.Println("ssh key pair creation complete") + progressPrinter.IncrementTracker("step-ssh", 1) + + repo.PrepareKubefirstTemplateRepo( + dryRun, + config, + viper.GetString("github.owner"), + viper.GetString("gitops.repo"), + viper.GetString("gitops.branch"), + viper.GetString("template.tag"), + ) + log.Println("clone and detokenization of gitops-template repository complete") + progressPrinter.IncrementTracker("step-gitops", 1) + + log.Println("sending init completed metric") + + pkg.InformUser("init is done!\n", silentMode) + // telemetry if useTelemetry { // Instantiates a SegmentIO client to send messages to the segment API. @@ -134,6 +158,8 @@ func runLocal(cmd *cobra.Command, args []string) error { if err != nil { log.Println(err) } + + progressPrinter.IncrementTracker("step-telemetry", 1) } // todo need to add go channel to control when ngrok should close @@ -145,8 +171,6 @@ func runLocal(cmd *cobra.Command, args []string) error { if viper.GetString("gitprovider") == "github" { log.Println("Installing Github version of Kubefirst") viper.Set("git.mode", "github") - // if not local it is AWS for now - // todo: internal err = k3d.CreateK3dCluster() if err != nil { return err @@ -155,26 +179,14 @@ func runLocal(cmd *cobra.Command, args []string) error { viper.Set("kubefirst.done", true) viper.WriteConfig() } - // start - //infoCmd need to be before the bars or it is printed in between bars: - //Let's try to not move it on refactors - //infoCmd.Run(cmd, args) var kPortForwardArgocd *exec.Cmd - progressPrinter.AddTracker("step-0", "Process Parameters", 1) - progressPrinter.AddTracker("step-github", "Setup gitops on github", 3) - progressPrinter.AddTracker("step-base", "Setup base cluster", 2) - progressPrinter.AddTracker("step-apps", "Install apps to cluster", 5) progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), silentMode) progressPrinter.IncrementTracker("step-0", 1) - if !useTelemetry { - pkg.InformUser("Telemetry Disabled", silentMode) - } - executionControl := viper.GetBool("terraform.github.apply.complete") - //* create github teams in the org and gitops repo + // create github teams in the org and gitops repo if !executionControl { pkg.InformUser("Creating github resources with terraform", silentMode) @@ -187,7 +199,7 @@ func runLocal(cmd *cobra.Command, args []string) error { log.Println("already created github terraform resources") } - //* push our locally detokenized gitops repo to remote github + // push our locally detokenized gitops repo to remote github githubHost := viper.GetString("github.host") githubOwner := viper.GetString("github.owner") localRepo := "gitops" @@ -201,7 +213,7 @@ func runLocal(cmd *cobra.Command, args []string) error { } progressPrinter.IncrementTracker("step-github", 1) - //* create kubernetes cluster + // create kubernetes cluster executionControl = viper.GetBool("k3d.created") if !executionControl { pkg.InformUser("Creating K8S Cluster", silentMode) @@ -244,13 +256,14 @@ func runLocal(cmd *cobra.Command, args []string) error { log.Println("already created initial argocd repository") } - //* helm add argo repository && update - helmRepo := helm.HelmRepo{} - helmRepo.RepoName = "argo" - helmRepo.RepoURL = "https://argoproj.github.io/argo-helm" - helmRepo.ChartName = "argo-cd" - helmRepo.Namespace = "argocd" - helmRepo.ChartVersion = "4.10.5" + // helm add argo repository && update + helmRepo := helm.HelmRepo{ + RepoName: pkg.HelmRepoName, + RepoURL: pkg.HelmRepoURL, + ChartName: pkg.HelmRepoChartName, + Namespace: pkg.HelmRepoNamespace, + ChartVersion: pkg.HelmRepoChartVersion, + } executionControl = viper.GetBool("argocd.helm.repo.updated") if !executionControl { @@ -258,7 +271,7 @@ func runLocal(cmd *cobra.Command, args []string) error { helm.AddRepoAndUpdateRepo(dryRun, helmRepo) } - //* helm install argocd + // helm install argocd executionControl = viper.GetBool("argocd.helm.install.complete") if !executionControl { pkg.InformUser(fmt.Sprintf("helm install %s and wait", helmRepo.RepoName), silentMode) @@ -266,7 +279,7 @@ func runLocal(cmd *cobra.Command, args []string) error { } progressPrinter.IncrementTracker("step-apps", 1) - //* argocd pods are running + // argocd pods are running executionControl = viper.GetBool("argocd.ready") if !executionControl { argocd.WaitArgoCDToBeReady(dryRun) @@ -275,7 +288,7 @@ func runLocal(cmd *cobra.Command, args []string) error { log.Println("already waited for argocd to be ready") } - //* establish port-forward + // establish port-forward kPortForwardArgocd, err = k8s.PortForward(dryRun, "argocd", "svc/argocd-server", "8080:80") defer func() { err = kPortForwardArgocd.Process.Signal(syscall.SIGTERM) @@ -285,7 +298,7 @@ func runLocal(cmd *cobra.Command, args []string) error { }() pkg.InformUser(fmt.Sprintf("port-forward to argocd is available at %s", viper.GetString("argocd.local.service")), silentMode) - //* argocd pods are ready, get and set credentials + // argocd pods are ready, get and set credentials executionControl = viper.GetBool("argocd.credentials.set") if !executionControl { pkg.InformUser("Setting argocd username and password credentials", silentMode) @@ -300,7 +313,7 @@ func runLocal(cmd *cobra.Command, args []string) error { viper.WriteConfig() } - //* argocd sync registry and start sync waves + // argocd sync registry and start sync waves executionControl = viper.GetBool("argocd.registry.applied") if !executionControl { pkg.InformUser("applying the registry application to argocd", silentMode) @@ -313,7 +326,7 @@ func runLocal(cmd *cobra.Command, args []string) error { progressPrinter.IncrementTracker("step-apps", 1) - //* vault in running state + // vault in running state executionControl = viper.GetBool("vault.status.running") if !executionControl { pkg.InformUser("Waiting for vault to be ready", silentMode) @@ -340,7 +353,7 @@ func runLocal(cmd *cobra.Command, args []string) error { } }() - //* configure vault with terraform + // configure vault with terraform executionControl = viper.GetBool("terraform.vault.apply.complete") if !executionControl { // todo evaluate progressPrinter.IncrementTracker("step-vault", 1) @@ -388,13 +401,11 @@ func runLocal(cmd *cobra.Command, args []string) error { pkg.InformUser("kubectl -n argocd port-forward svc/argocd-server 8080:80", silentMode) pkg.InformUser("Argo User: "+viper.GetString("argocd.admin.username"), silentMode) pkg.InformUser("Argo Password: "+viper.GetString("argocd.admin.password"), silentMode) - time.Sleep(1 * time.Second) + progressPrinter.IncrementTracker("step-apps", 1) progressPrinter.IncrementTracker("step-base", 1) progressPrinter.IncrementTracker("step-apps", 1) - // end - if !viper.GetBool("chartmuseum.host.resolved") { //* establish port-forward @@ -433,7 +444,7 @@ func runLocal(cmd *cobra.Command, args []string) error { // --- // update terraform s3 backend to internal k8s dns (s3/minio bucket) - err = pkg.ReplaceS3Backend() + err = pkg.ReplaceTerraformS3Backend() if err != nil { return err } @@ -446,37 +457,44 @@ func runLocal(cmd *cobra.Command, args []string) error { branchName := "update-s3-backend" branchNameRef := plumbing.ReferenceName("refs/heads/" + branchName) - gitClient.UpdateLocalTFFilesAndPush( + err = gitClient.UpdateLocalTerraformFilesAndPush( githubHost, githubOwner, localRepo, remoteName, branchNameRef, ) + if err != nil { + log.Println(err) + } fmt.Println("sleeping after commit...") time.Sleep(3 * time.Second) // create a PR, atlantis will identify it's a terraform change/file update and, // trigger atlantis plan - g := githubWrapper.New() - err = g.CreatePR(branchName) - if err != nil { - fmt.Println(err) - } - log.Println("sleeping after create PR...") - time.Sleep(5 * time.Second) - log.Println("sleeping... atlantis plan should be running") - time.Sleep(5 * time.Second) - - fmt.Println("sleeping before apply...") - time.Sleep(120 * time.Second) - - // after 120 seconds, it will comment in the PR with atlantis plan - err = g.CommentPR(1, "atlantis apply") - if err != nil { - fmt.Println(err) - } + var wg sync.WaitGroup + wg.Add(1) + go func() { + gitHubClient := githubWrapper.New() + err = gitHubClient.CreatePR(branchName) + if err != nil { + fmt.Println(err) + } + log.Println("sleeping after create PR, atlantis plan should be running...") + time.Sleep(5 * time.Second) + + // todo: confirm apply isn't necessary + //fmt.Println("sleeping before apply...") + //time.Sleep(120 * time.Second) + // + //// after 120 seconds, it will comment in the PR with atlantis plan + //err = gitHubClient.CommentPR(1, "atlantis apply") + //if err != nil { + // log.Println(err) + //} + wg.Done() + }() log.Println("sending mgmt cluster install completed metric") @@ -521,86 +539,19 @@ func runLocal(cmd *cobra.Command, args []string) error { // return err //} + // waiting GitHub/atlantis step + wg.Wait() + return nil } -func NewInit(gitopsRepo string, gitopsOwner string, gitProvider string, metaphorBranch string) error { +func validateLocal(cmd *cobra.Command, args []string) error { - //tools.RunInfo(cmd, args) - // todo: load it from viper config := configs.ReadConfig() - // set default values - viper.Set("gitops.repo", gitOpsRepo) - // todo: do we need it? - viper.Set("gitops.owner", "kubefirst") - - viper.Set("gitprovider", gitProvider) - viper.Set("metaphor.branch", metaphorBranch) - viper.WriteConfig() - // - if err := pkg.ValidateK1Folder(config.K1FolderPath); err != nil { - return err - } - - // todo: wrap business logic into the handler - if config.GitHubPersonalAccessToken == "" { - - httpClient := http.DefaultClient - gitHubService := services.NewGitHubService(httpClient) - gitHubHandler := handlers.NewGitHubHandler(gitHubService) - gitHubAccessToken, err := gitHubHandler.AuthenticateUser() - if err != nil { - return err - } - - if len(gitHubAccessToken) == 0 { - return errors.New("unable to retrieve a GitHub token for the user") - } - - viper.Set("github.token", gitHubAccessToken) - err = viper.WriteConfig() - if err != nil { - return err - } - - // todo: set common way to load env. values (viper->struct->load-env) - // todo: use viper file to load it, not load env. value - if err := os.Setenv("GITHUB_AUTH_TOKEN", gitHubAccessToken); err != nil { - return err - } - log.Println("\nGITHUB_AUTH_TOKEN set via OAuth") - } - - // review it - viper.Set("gitops.branch", gitOpsBranch) - viper.Set("github.owner", viper.GetString("github.user")) - viper.Set("cloud", pkg.CloudK3d) - viper.Set("cluster-name", clusterName) - viper.Set("adminemail", adminEmail) - viper.WriteConfig() - - if silentMode { - pkg.InformUser( - "Silent mode enabled, most of the UI prints wont be showed. Please check the logs for more details.\n", - silentMode, - ) - } - - progressPrinter.AddTracker("step-download", pkg.DownloadDependencies, 3) - progressPrinter.AddTracker("step-gitops", pkg.CloneAndDetokenizeGitOpsTemplate, 1) - progressPrinter.AddTracker("step-ssh", pkg.CreateSSHKey, 1) - progressPrinter.AddTracker("step-telemetry", pkg.SendTelemetry, 1) - - progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), silentMode) - - log.Println("sending init started metric") - - // todo: var telemetryHandler handlers.TelemetryHandler if useTelemetry { - // Instantiates a SegmentIO client to use send messages to the segment API. segmentIOClient := analytics.New(pkg.SegmentIOWriteKey) @@ -631,6 +582,22 @@ func NewInit(gitopsRepo string, gitopsOwner string, gitProvider string, metaphor } } + if err := pkg.ValidateK1Folder(config.K1FolderPath); err != nil { + return err + } + + // set default values to kubefirst file + viper.Set("gitops.repo", gitOpsRepo) + viper.Set("gitops.owner", "kubefirst") + viper.Set("gitprovider", pkg.GitHubProviderName) + viper.Set("metaphor.branch", metaphorBranch) + + viper.Set("gitops.branch", gitOpsBranch) + viper.Set("github.owner", viper.GetString("github.user")) + viper.Set("cloud", pkg.CloudK3d) + viper.Set("cluster-name", pkg.LocalClusterName) + viper.Set("adminemail", adminEmail) + // todo: set constants viper.Set("argocd.local.service", "http://localhost:8080") viper.Set("gitlab.local.service", "http://localhost:8888") @@ -642,36 +609,46 @@ func NewInit(gitopsRepo string, gitopsOwner string, gitProvider string, metaphor viper.WriteConfig() - //! tracker 0 - log.Println("installing kubefirst dependencies") - progressPrinter.IncrementTracker("step-download", 1) - err := downloadManager.DownloadTools(config) - if err != nil { - return err - } - log.Println("dependency installation complete") - progressPrinter.IncrementTracker("step-download", 1) - err = downloadManager.DownloadLocalTools(config) + err := viper.WriteConfig() if err != nil { return err } - //Fix incomplete bar, please don't remove it. - progressPrinter.IncrementTracker("step-download", 1) + // todo: wrap business logic into the handler + if config.GitHubPersonalAccessToken == "" { - //! tracker 5 - log.Println("creating an ssh key pair for your new cloud infrastructure") - pkg.CreateSshKeyPair() - log.Println("ssh key pair creation complete") - progressPrinter.IncrementTracker("step-ssh", 1) + httpClient := http.DefaultClient + gitHubService := services.NewGitHubService(httpClient) + gitHubHandler := handlers.NewGitHubHandler(gitHubService) + gitHubAccessToken, err := gitHubHandler.AuthenticateUser() + if err != nil { + return err + } - //! tracker 6 + if len(gitHubAccessToken) == 0 { + return errors.New("unable to retrieve a GitHub token for the user") + } - repo.PrepareKubefirstTemplateRepo(dryRun, config, viper.GetString("github.owner"), viper.GetString("gitops.repo"), viper.GetString("gitops.branch"), viper.GetString("template.tag")) - log.Println("clone and detokenization of gitops-template repository complete") - progressPrinter.IncrementTracker("step-gitops", 1) + viper.Set("github.token", gitHubAccessToken) + err = viper.WriteConfig() + if err != nil { + return err + } - log.Println("sending init completed metric") + // todo: set common way to load env. values (viper->struct->load-env) + // todo: use viper file to load it, not load env. value + if err := os.Setenv("GITHUB_AUTH_TOKEN", gitHubAccessToken); err != nil { + return err + } + log.Println("\nGITHUB_AUTH_TOKEN set via OAuth") + } + + if silentMode { + pkg.InformUser( + "Silent mode enabled, most of the UI prints wont be showed. Please check the logs for more details.\n", + silentMode, + ) + } if useTelemetry { telemetryInitCompleted, err := domain.NewTelemetry( @@ -688,13 +665,5 @@ func NewInit(gitopsRepo string, gitopsOwner string, gitProvider string, metaphor } } - viper.WriteConfig() - - //! tracker 8 - progressPrinter.IncrementTracker("step-telemetry", 1) - time.Sleep(time.Millisecond * 100) - - pkg.InformUser("init is done!\n", silentMode) - return nil } diff --git a/internal/gitClient/git.go b/internal/gitClient/git.go index 7305f0596..bcf0250f7 100644 --- a/internal/gitClient/git.go +++ b/internal/gitClient/git.go @@ -402,7 +402,7 @@ func PushLocalRepoUpdates(githubHost, githubOwner, localRepo, remoteName string) } // todo: refactor -func UpdateLocalTFFilesAndPush(githubHost, githubOwner, localRepo, remoteName string, branchDestiny plumbing.ReferenceName) { +func UpdateLocalTerraformFilesAndPush(githubHost, githubOwner, localRepo, remoteName string, branchDestiny plumbing.ReferenceName) error { cfg := configs.ReadConfig() @@ -419,16 +419,12 @@ func UpdateLocalTFFilesAndPush(githubHost, githubOwner, localRepo, remoteName st url := fmt.Sprintf("https://%s/%s/%s", githubHost, githubOwner, localRepo) log.Printf("git push to remote: %s url: %s", remoteName, url) - w, _ := repo.Worktree() - - //headRef, err := repo.Head() - //ref := plumbing.NewHashReference(branchDestiny, headRef.Hash()) - //if err = repo.Storer.SetReference(ref); err != nil { - // log.Panic(err) - //} + w, err := repo.Worktree() + if err != nil { + return err + } err = w.Checkout(&git.CheckoutOptions{ - //Branch: plumbing.ReferenceName("ref/heads/update-s3-backend"), Branch: branchDestiny, Create: true, }) @@ -437,19 +433,6 @@ func UpdateLocalTFFilesAndPush(githubHost, githubOwner, localRepo, remoteName st } log.Println("Committing new changes... PushLocalRepoUpdates") - //status, err := w.Status() - //if err != nil { - // log.Println("error getting worktree status", err) - //} - - //for file, s := range status { - // //log.Printf("the file is %s the status is %v", file, s.Worktree) - // fmt.Printf("the file is %s the status is %v", file, s.Worktree) - // _, err = w.Add(file) - // if err != nil { - // log.Println("error getting worktree status", err) - // } - //} if viper.GetString("gitprovider") == "github" { kubefirstGitHubFile := "terraform/users/kubefirst-github.tf" @@ -487,4 +470,6 @@ func UpdateLocalTFFilesAndPush(githubHost, githubOwner, localRepo, remoteName st log.Panicf("error pushing to remote %s: %s", remoteName, err) } log.Println("successfully pushed detokenized gitops content to github/", viper.GetString("github.owner")) + + return nil } diff --git a/pkg/constants.go b/pkg/constants.go index e76b2fc35..a6d3afd8d 100644 --- a/pkg/constants.go +++ b/pkg/constants.go @@ -9,6 +9,7 @@ const ( GitHubOAuthClientId = "2ced340927e0a6c49a45" CloudK3d = "k3d" GitHubProviderName = "github" + LocalClusterName = "kubefirst" ) // SegmentIO constants @@ -21,3 +22,11 @@ const ( MetricMgmtClusterInstallStarted = "kubefirst.mgmt_cluster_install.started" MetricMgmtClusterInstallCompleted = "kubefirst.mgmt_cluster_install.completed" ) + +const ( + HelmRepoName = "argo" + HelmRepoURL = "https://argoproj.github.io/argo-helm" + HelmRepoChartName = "argo-cd" + HelmRepoNamespace = "argocd" + HelmRepoChartVersion = "4.10.5" +) diff --git a/pkg/helpers.go b/pkg/helpers.go index 8810c548b..4048e09c2 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -446,7 +446,7 @@ func AwaitHostNTimes(url string, times int, gracePeriod time.Duration) { // } // this is temporary code -func ReplaceS3Backend() error { +func ReplaceTerraformS3Backend() error { config := configs.ReadConfig() From 100b8a0690b11eacd1e29594868099ad767419c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 2 Nov 2022 10:57:27 -0300 Subject: [PATCH 3/3] feat: split cobra functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/local/local.go | 246 +++++----------------------------- cmd/local/postrun.go | 63 +++++++++ cmd/local/prerun.go | 185 +++++++++++++++++++++++++ cmd/postInstall.go | 151 ++------------------- internal/k8s/kubernetes.go | 82 ++++++++++++ internal/metaphor/metaphor.go | 51 +++++++ pkg/constants.go | 1 + pkg/helpers.go | 52 +++++++ 8 files changed, 475 insertions(+), 356 deletions(-) create mode 100644 cmd/local/postrun.go create mode 100644 cmd/local/prerun.go diff --git a/cmd/local/local.go b/cmd/local/local.go index 7b78efdcb..381f0725b 100644 --- a/cmd/local/local.go +++ b/cmd/local/local.go @@ -2,21 +2,19 @@ package local import ( "context" - "errors" "fmt" "github.com/go-git/go-git/v5/plumbing" "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/internal/argocd" "github.com/kubefirst/kubefirst/internal/domain" - "github.com/kubefirst/kubefirst/internal/downloadManager" "github.com/kubefirst/kubefirst/internal/gitClient" "github.com/kubefirst/kubefirst/internal/githubWrapper" "github.com/kubefirst/kubefirst/internal/handlers" "github.com/kubefirst/kubefirst/internal/helm" "github.com/kubefirst/kubefirst/internal/k3d" "github.com/kubefirst/kubefirst/internal/k8s" + "github.com/kubefirst/kubefirst/internal/metaphor" "github.com/kubefirst/kubefirst/internal/progressPrinter" - "github.com/kubefirst/kubefirst/internal/repo" "github.com/kubefirst/kubefirst/internal/services" "github.com/kubefirst/kubefirst/internal/terraform" "github.com/kubefirst/kubefirst/internal/vault" @@ -25,8 +23,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "log" - "net/http" - "os" "os/exec" "sync" "syscall" @@ -42,27 +38,32 @@ var ( awsHostedZone string metaphorBranch string adminEmail string + enableConsole bool ) func NewCommand() *cobra.Command { localCmd := &cobra.Command{ - Use: "local", - Short: "Kubefirst localhost installation", - Long: "Kubefirst localhost enable a localhost installation without the requirement of a cloud provider.", - PreRunE: validateLocal, - RunE: runLocal, + Use: "local", + Short: "Kubefirst localhost installation", + Long: "Kubefirst localhost enable a localhost installation without the requirement of a cloud provider.", + PreRunE: validateLocal, + RunE: runLocal, + PostRunE: runPostLocal, } - localCmd.Flags().BoolVar(&useTelemetry, "use-telemetry", true, "xinstaller will not send telemetry about this installation") + localCmd.Flags().BoolVar(&useTelemetry, "use-telemetry", true, "installer will not send telemetry about this installation") localCmd.Flags().BoolVar(&dryRun, "dry-run", false, "set to dry-run mode, no changes done on cloud provider selected") localCmd.Flags().BoolVar(&silentMode, "silent", false, "enable silentMode mode will make the UI return less content to the screen") - // todo: get it from GH token + // todo: get it from GH token , use it for console localCmd.Flags().StringVar(&adminEmail, "admin-email", "", "the email address for the administrator as well as for lets-encrypt certificate emails") localCmd.Flags().StringVar(&metaphorBranch, "metaphor-branch", "main", "metaphro application branch") localCmd.Flags().StringVar(&gitOpsBranch, "gitops-branch", "main", "version/branch used on git clone - former: version-gitops flag") localCmd.Flags().StringVar(&gitOpsRepo, "gitops-repo", "gitops", "") + + localCmd.Flags().BoolVar(&enableConsole, "enable-console", true, "If hand-off screen will be presented on a browser UI") + // todo: //initCmd.Flags().StringP("config", "c", "", "File to be imported to bootstrap configs") //viper.BindPFlag("config.file", currentCommand.Flags().Lookup("config-load")) @@ -75,10 +76,6 @@ func runLocal(cmd *cobra.Command, args []string) error { //tools.RunInfo(cmd, args) - progressPrinter.AddTracker("step-download", pkg.DownloadDependencies, 3) - progressPrinter.AddTracker("step-gitops", pkg.CloneAndDetokenizeGitOpsTemplate, 1) - progressPrinter.AddTracker("step-ssh", pkg.CreateSSHKey, 1) - progressPrinter.AddTracker("step-0", "Process Parameters", 1) progressPrinter.AddTracker("step-github", "Setup gitops on github", 3) progressPrinter.AddTracker("step-base", "Setup base cluster", 2) progressPrinter.AddTracker("step-apps", "Install apps to cluster", 5) @@ -92,43 +89,6 @@ func runLocal(cmd *cobra.Command, args []string) error { progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), silentMode) - log.Println("sending init started metric") - - log.Println("installing kubefirst dependencies") - progressPrinter.IncrementTracker("step-download", 1) - err := downloadManager.DownloadTools(config) - if err != nil { - return err - } - log.Println("dependency installation complete") - progressPrinter.IncrementTracker("step-download", 1) - err = downloadManager.DownloadLocalTools(config) - if err != nil { - return err - } - - progressPrinter.IncrementTracker("step-download", 1) - - log.Println("creating an ssh key pair for your new cloud infrastructure") - pkg.CreateSshKeyPair() - log.Println("ssh key pair creation complete") - progressPrinter.IncrementTracker("step-ssh", 1) - - repo.PrepareKubefirstTemplateRepo( - dryRun, - config, - viper.GetString("github.owner"), - viper.GetString("gitops.repo"), - viper.GetString("gitops.branch"), - viper.GetString("template.tag"), - ) - log.Println("clone and detokenization of gitops-template repository complete") - progressPrinter.IncrementTracker("step-gitops", 1) - - log.Println("sending init completed metric") - - pkg.InformUser("init is done!\n", silentMode) - // telemetry if useTelemetry { // Instantiates a SegmentIO client to send messages to the segment API. @@ -171,7 +131,7 @@ func runLocal(cmd *cobra.Command, args []string) error { if viper.GetString("gitprovider") == "github" { log.Println("Installing Github version of Kubefirst") viper.Set("git.mode", "github") - err = k3d.CreateK3dCluster() + err := k3d.CreateK3dCluster() if err != nil { return err } @@ -183,8 +143,6 @@ func runLocal(cmd *cobra.Command, args []string) error { var kPortForwardArgocd *exec.Cmd progressPrinter.SetupProgress(progressPrinter.TotalOfTrackers(), silentMode) - progressPrinter.IncrementTracker("step-0", 1) - executionControl := viper.GetBool("terraform.github.apply.complete") // create github teams in the org and gitops repo if !executionControl { @@ -217,7 +175,7 @@ func runLocal(cmd *cobra.Command, args []string) error { executionControl = viper.GetBool("k3d.created") if !executionControl { pkg.InformUser("Creating K8S Cluster", silentMode) - err = k3d.CreateK3dCluster() + err := k3d.CreateK3dCluster() if err != nil { log.Println("Error installing k3d cluster") return err @@ -232,7 +190,7 @@ func runLocal(cmd *cobra.Command, args []string) error { // todo there is a secret condition in AddK3DSecrets to this not checked executionControl = viper.GetBool("kubernetes.vault.secret.created") if !executionControl { - err = k3d.AddK3DSecrets(dryRun) + err := k3d.AddK3DSecrets(dryRun) if err != nil { log.Println("Error AddK3DSecrets") return err @@ -247,7 +205,7 @@ func runLocal(cmd *cobra.Command, args []string) error { pkg.InformUser("create initial argocd repository", silentMode) //Enterprise users need to be able to set the hostname for git. gitopsRepo := fmt.Sprintf("git@%s:%s/gitops.git", viper.GetString("github.host"), viper.GetString("github.owner")) - err = argocd.CreateInitialArgoCDRepository(gitopsRepo) + err := argocd.CreateInitialArgoCDRepository(gitopsRepo) if err != nil { log.Println("Error CreateInitialArgoCDRepository") return err @@ -289,7 +247,7 @@ func runLocal(cmd *cobra.Command, args []string) error { } // establish port-forward - kPortForwardArgocd, err = k8s.PortForward(dryRun, "argocd", "svc/argocd-server", "8080:80") + kPortForwardArgocd, err := k8s.PortForward(dryRun, "argocd", "svc/argocd-server", "8080:80") defer func() { err = kPortForwardArgocd.Process.Signal(syscall.SIGTERM) if err != nil { @@ -377,7 +335,7 @@ func runLocal(cmd *cobra.Command, args []string) error { log.Println("already executed vault terraform") } - //* create users + // create users executionControl = viper.GetBool("terraform.users.apply.complete") if !executionControl { pkg.InformUser("applying users terraform", silentMode) @@ -424,24 +382,13 @@ func runLocal(cmd *cobra.Command, args []string) error { log.Println("already resolved host for chartmuseum, continuing") } - // todo: uncomment it - //pkg.InformUser("Deploying metaphor applications", silentMode) - //err = deployMetaphorCmd.RunE(cmd, args) - //if err != nil { - // pkg.InformUser("Error deploy metaphor applications", silentMode) - // log.Println("Error running deployMetaphorCmd") - // return err - //} - - //kPortForwardAtlantis, err := k8s.PortForward(dryRun, "atlantis", "svc/atlantis", "4141:80") - //defer func() { - // err = kPortForwardAtlantis.Process.Signal(syscall.SIGTERM) - // if err != nil { - // log.Println("error closing kPortForwardAtlantis") - // } - //}() - - // --- + pkg.InformUser("Deploying metaphor applications", silentMode) + err = metaphor.DeployMetaphorGithubLocal(dryRun, githubOwner, metaphorBranch, "") + if err != nil { + pkg.InformUser("Error deploy metaphor applications", silentMode) + log.Println("Error running deployMetaphorCmd") + log.Println(err) + } // update terraform s3 backend to internal k8s dns (s3/minio bucket) err = pkg.ReplaceTerraformS3Backend() @@ -450,13 +397,10 @@ func runLocal(cmd *cobra.Command, args []string) error { } // create a new branch and push changes - githubHost = viper.GetString("github.host") - githubOwner = viper.GetString("github.owner") - remoteName = "github" - localRepo = "gitops" branchName := "update-s3-backend" branchNameRef := plumbing.ReferenceName("refs/heads/" + branchName) + // force update cloned gitops-template terraform files to use Minio backend err = gitClient.UpdateLocalTerraformFilesAndPush( githubHost, githubOwner, @@ -468,11 +412,11 @@ func runLocal(cmd *cobra.Command, args []string) error { log.Println(err) } - fmt.Println("sleeping after commit...") + log.Println("sleeping after git commit with Minio backend update for Terraform") time.Sleep(3 * time.Second) - // create a PR, atlantis will identify it's a terraform change/file update and, - // trigger atlantis plan + // create a PR, atlantis will identify it's a Terraform change/file update and trigger atlantis plan + // it's a goroutine since it can run in background var wg sync.WaitGroup wg.Add(1) go func() { @@ -531,139 +475,9 @@ func runLocal(cmd *cobra.Command, args []string) error { log.Println("Kubefirst installation finished successfully") pkg.InformUser("Kubefirst installation finished successfully", silentMode) - // todo: temporary code to enable console for localhost / enable it back! - //err = postInstallCmd.RunE(cmd, args) - //if err != nil { - // pkg.InformUser("Error starting apps from post-install", silentMode) - // log.Println("Error running postInstallCmd") - // return err - //} - // waiting GitHub/atlantis step wg.Wait() return nil } - -func validateLocal(cmd *cobra.Command, args []string) error { - - config := configs.ReadConfig() - - var telemetryHandler handlers.TelemetryHandler - if useTelemetry { - // Instantiates a SegmentIO client to use send messages to the segment API. - segmentIOClient := analytics.New(pkg.SegmentIOWriteKey) - - // SegmentIO library works with queue that is based on timing, we explicit close the http client connection - // to force flush in case there is still some pending message in the SegmentIO library queue. - defer func(segmentIOClient analytics.Client) { - err := segmentIOClient.Close() - if err != nil { - log.Println(err) - } - }(segmentIOClient) - - // validate telemetryDomain data - telemetryDomain, err := domain.NewTelemetry( - pkg.MetricInitStarted, - awsHostedZone, - configs.K1Version, - ) - if err != nil { - log.Println(err) - } - telemetryService := services.NewSegmentIoService(segmentIOClient) - telemetryHandler = handlers.NewTelemetryHandler(telemetryService) - - err = telemetryHandler.SendCountMetric(telemetryDomain) - if err != nil { - log.Println(err) - } - } - - if err := pkg.ValidateK1Folder(config.K1FolderPath); err != nil { - return err - } - - // set default values to kubefirst file - viper.Set("gitops.repo", gitOpsRepo) - viper.Set("gitops.owner", "kubefirst") - viper.Set("gitprovider", pkg.GitHubProviderName) - viper.Set("metaphor.branch", metaphorBranch) - - viper.Set("gitops.branch", gitOpsBranch) - viper.Set("github.owner", viper.GetString("github.user")) - viper.Set("cloud", pkg.CloudK3d) - viper.Set("cluster-name", pkg.LocalClusterName) - viper.Set("adminemail", adminEmail) - - // todo: set constants - viper.Set("argocd.local.service", "http://localhost:8080") - viper.Set("gitlab.local.service", "http://localhost:8888") - viper.Set("vault.local.service", "http://localhost:8200") - // used for letsencrypt notifications and the gitlab root account - - atlantisWebhookSecret := pkg.Random(20) - viper.Set("github.atlantis.webhook.secret", atlantisWebhookSecret) - - viper.WriteConfig() - - err := viper.WriteConfig() - if err != nil { - return err - } - - // todo: wrap business logic into the handler - if config.GitHubPersonalAccessToken == "" { - - httpClient := http.DefaultClient - gitHubService := services.NewGitHubService(httpClient) - gitHubHandler := handlers.NewGitHubHandler(gitHubService) - gitHubAccessToken, err := gitHubHandler.AuthenticateUser() - if err != nil { - return err - } - - if len(gitHubAccessToken) == 0 { - return errors.New("unable to retrieve a GitHub token for the user") - } - - viper.Set("github.token", gitHubAccessToken) - err = viper.WriteConfig() - if err != nil { - return err - } - - // todo: set common way to load env. values (viper->struct->load-env) - // todo: use viper file to load it, not load env. value - if err := os.Setenv("GITHUB_AUTH_TOKEN", gitHubAccessToken); err != nil { - return err - } - log.Println("\nGITHUB_AUTH_TOKEN set via OAuth") - } - - if silentMode { - pkg.InformUser( - "Silent mode enabled, most of the UI prints wont be showed. Please check the logs for more details.\n", - silentMode, - ) - } - - if useTelemetry { - telemetryInitCompleted, err := domain.NewTelemetry( - pkg.MetricInitCompleted, - awsHostedZone, - configs.K1Version, - ) - if err != nil { - log.Println(err) - } - err = telemetryHandler.SendCountMetric(telemetryInitCompleted) - if err != nil { - log.Println(err) - } - } - - return nil -} diff --git a/cmd/local/postrun.go b/cmd/local/postrun.go new file mode 100644 index 000000000..6e0bcdeb6 --- /dev/null +++ b/cmd/local/postrun.go @@ -0,0 +1,63 @@ +package local + +import ( + "fmt" + "github.com/kubefirst/kubefirst/configs" + sw "github.com/kubefirst/kubefirst/internal/api" + "github.com/kubefirst/kubefirst/internal/k8s" + "github.com/kubefirst/kubefirst/internal/reports" + "github.com/kubefirst/kubefirst/pkg" + "github.com/spf13/cobra" + "log" + "net/http" + "time" +) + +func runPostLocal(cmd *cobra.Command, args []string) error { + + if !enableConsole { + log.Println("not calling console, console flag is disabled") + return nil + } + + // open all port forwards, wait console ui be ready, and open console ui in the browser + err := k8s.OpenPortForwardForKubeConConsole() + if err != nil { + log.Println(err) + } + + time.Sleep(time.Millisecond * 2000) + + log.Println("Starting the presentation of console and api for the handoff screen") + + // todo: fix the memory leak, there is no joint point after the process fork + go func() { + log.Printf("Console API started") + consoleApiRouter := sw.NewRouter() + log.Println(http.ListenAndServe(":9095", consoleApiRouter)) + }() + go func() { + config := configs.ReadConfig() + distFolder := fmt.Sprintf("%s/tools/console/dist", config.K1FolderPath) + fileServer := http.FileServer(http.Dir(distFolder)) + http.Handle("/", fileServer) + + log.Printf("Starting server at port 9094\n") + log.Println(http.ListenAndServe(":9094", nil)) + }() + + err = pkg.IsConsoleUIAvailable(pkg.LocalConsoleUI) + if err != nil { + log.Println(err) + } + err = pkg.OpenBrowser(pkg.LocalConsoleUI) + if err != nil { + return err + } + + reports.LocalHandoffScreen(dryRun, silentMode) + + log.Println("Kubefirst Console available at: http://localhost:9094", silentMode) + + return nil +} diff --git a/cmd/local/prerun.go b/cmd/local/prerun.go new file mode 100644 index 000000000..dbc121797 --- /dev/null +++ b/cmd/local/prerun.go @@ -0,0 +1,185 @@ +package local + +import ( + "errors" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/domain" + "github.com/kubefirst/kubefirst/internal/downloadManager" + "github.com/kubefirst/kubefirst/internal/handlers" + "github.com/kubefirst/kubefirst/internal/progressPrinter" + "github.com/kubefirst/kubefirst/internal/repo" + "github.com/kubefirst/kubefirst/internal/services" + "github.com/kubefirst/kubefirst/pkg" + "github.com/segmentio/analytics-go" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "log" + "net/http" + "os" +) + +func validateLocal(cmd *cobra.Command, args []string) error { + + config := configs.ReadConfig() + + progressPrinter.AddTracker("step-0", "Process Parameters", 1) + progressPrinter.AddTracker("step-download", pkg.DownloadDependencies, 3) + progressPrinter.AddTracker("step-gitops", pkg.CloneAndDetokenizeGitOpsTemplate, 1) + progressPrinter.AddTracker("step-ssh", pkg.CreateSSHKey, 1) + + log.Println("sending init started metric") + + var telemetryHandler handlers.TelemetryHandler + if useTelemetry { + // Instantiates a SegmentIO client to use send messages to the segment API. + segmentIOClient := analytics.New(pkg.SegmentIOWriteKey) + + // SegmentIO library works with queue that is based on timing, we explicit close the http client connection + // to force flush in case there is still some pending message in the SegmentIO library queue. + defer func(segmentIOClient analytics.Client) { + err := segmentIOClient.Close() + if err != nil { + log.Println(err) + } + }(segmentIOClient) + + // validate telemetryDomain data + telemetryDomain, err := domain.NewTelemetry( + pkg.MetricInitStarted, + awsHostedZone, + configs.K1Version, + ) + if err != nil { + log.Println(err) + } + telemetryService := services.NewSegmentIoService(segmentIOClient) + telemetryHandler = handlers.NewTelemetryHandler(telemetryService) + + err = telemetryHandler.SendCountMetric(telemetryDomain) + if err != nil { + log.Println(err) + } + } + + if err := pkg.ValidateK1Folder(config.K1FolderPath); err != nil { + return err + } + + // set default values to kubefirst file + viper.Set("gitops.repo", gitOpsRepo) + viper.Set("gitops.owner", "kubefirst") + viper.Set("gitprovider", pkg.GitHubProviderName) + viper.Set("metaphor.branch", metaphorBranch) + + viper.Set("gitops.branch", gitOpsBranch) + viper.Set("github.owner", viper.GetString("github.user")) + viper.Set("cloud", pkg.CloudK3d) + viper.Set("cluster-name", pkg.LocalClusterName) + viper.Set("adminemail", adminEmail) + + // todo: set constants + viper.Set("argocd.local.service", "http://localhost:8080") + viper.Set("gitlab.local.service", "http://localhost:8888") + viper.Set("vault.local.service", "http://localhost:8200") + // used for letsencrypt notifications and the gitlab root account + + atlantisWebhookSecret := pkg.Random(20) + viper.Set("github.atlantis.webhook.secret", atlantisWebhookSecret) + + viper.WriteConfig() + + err := viper.WriteConfig() + if err != nil { + return err + } + + // todo: wrap business logic into the handler + if config.GitHubPersonalAccessToken == "" { + + httpClient := http.DefaultClient + gitHubService := services.NewGitHubService(httpClient) + gitHubHandler := handlers.NewGitHubHandler(gitHubService) + gitHubAccessToken, err := gitHubHandler.AuthenticateUser() + if err != nil { + return err + } + + if len(gitHubAccessToken) == 0 { + return errors.New("unable to retrieve a GitHub token for the user") + } + + viper.Set("github.token", gitHubAccessToken) + err = viper.WriteConfig() + if err != nil { + return err + } + + // todo: set common way to load env. values (viper->struct->load-env) + // todo: use viper file to load it, not load env. value + if err := os.Setenv("GITHUB_AUTH_TOKEN", gitHubAccessToken); err != nil { + return err + } + log.Println("\nGITHUB_AUTH_TOKEN set via OAuth") + } + + if silentMode { + pkg.InformUser( + "Silent mode enabled, most of the UI prints wont be showed. Please check the logs for more details.\n", + silentMode, + ) + } + + log.Println("installing kubefirst dependencies") + progressPrinter.IncrementTracker("step-download", 1) + err = downloadManager.DownloadTools(config) + if err != nil { + return err + } + log.Println("dependency installation complete") + progressPrinter.IncrementTracker("step-download", 1) + err = downloadManager.DownloadLocalTools(config) + if err != nil { + return err + } + + progressPrinter.IncrementTracker("step-download", 1) + + log.Println("creating an ssh key pair for your new cloud infrastructure") + pkg.CreateSshKeyPair() + log.Println("ssh key pair creation complete") + progressPrinter.IncrementTracker("step-ssh", 1) + + repo.PrepareKubefirstTemplateRepo( + dryRun, + config, + viper.GetString("github.owner"), + viper.GetString("gitops.repo"), + viper.GetString("gitops.branch"), + viper.GetString("template.tag"), + ) + log.Println("clone and detokenization of gitops-template repository complete") + progressPrinter.IncrementTracker("step-gitops", 1) + + log.Println("sending init completed metric") + + pkg.InformUser("init is done!\n", silentMode) + + if useTelemetry { + telemetryInitCompleted, err := domain.NewTelemetry( + pkg.MetricInitCompleted, + awsHostedZone, + configs.K1Version, + ) + if err != nil { + log.Println(err) + } + err = telemetryHandler.SendCountMetric(telemetryInitCompleted) + if err != nil { + log.Println(err) + } + } + + progressPrinter.IncrementTracker("step-0", 1) + + return nil +} diff --git a/cmd/postInstall.go b/cmd/postInstall.go index bf26371b9..4f5123f83 100644 --- a/cmd/postInstall.go +++ b/cmd/postInstall.go @@ -1,15 +1,10 @@ package cmd import ( - "fmt" + "github.com/kubefirst/kubefirst/internal/k8s" "log" - "net/http" - "runtime" - "sync" "time" - "github.com/kubefirst/kubefirst/internal/k8s" - "github.com/kubefirst/kubefirst/internal/flagset" "github.com/kubefirst/kubefirst/internal/reports" @@ -59,7 +54,10 @@ var postInstallCmd = &cobra.Command{ log.Println("Kubefirst Console available at: http://localhost:9094", globalFlags.SilentMode) - openbrowser(pkg.LocalConsoleUI) + err := pkg.OpenBrowser(pkg.LocalConsoleUI) + if err != nil { + return err + } } else { log.Println("Skipping the presentation of console and api for the handoff screen") @@ -67,16 +65,19 @@ var postInstallCmd = &cobra.Command{ // open all port forwards, wait console ui be ready, and open console ui in the browser if cloud == pkg.CloudK3d { - err := openPortForwardForKubeConConsole() + err := k8s.OpenPortForwardForKubeConConsole() if err != nil { log.Println(err) } - err = isConsoleUIAvailable(pkg.LocalConsoleUI) + err = pkg.IsConsoleUIAvailable(pkg.LocalConsoleUI) if err != nil { log.Println(err) } - openbrowser(pkg.LocalConsoleUI) + err = pkg.OpenBrowser(pkg.LocalConsoleUI) + if err != nil { + return err + } } if viper.GetString("cloud") == flagset.CloudK3d { @@ -98,133 +99,3 @@ func init() { //postInstallCmd.Flags().Bool("enable-console", true, "If hand-off screen will be presented on a browser UI") //flagset.DefineCreateFlags(currentCommand) } - -func openbrowser(url string) { - var err error - - switch runtime.GOOS { - case "linux": - _, _, err = pkg.ExecShellReturnStrings("xdg-open", url) - case "windows": - _, _, err = pkg.ExecShellReturnStrings("rundll32", "url.dll,FileProtocolHandler", url) - case "darwin": - _, _, err = pkg.ExecShellReturnStrings("open", url) - default: - err = fmt.Errorf("unsupported platform") - } - if err != nil { - log.Println(err) - } -} - -// todo: this is temporary -func isConsoleUIAvailable(url string) error { - attempts := 10 - httpClient := http.DefaultClient - for i := 0; i < attempts; i++ { - - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - log.Printf("unable to reach %q (%d/%d)", url, i+1, attempts) - time.Sleep(5 * time.Second) - continue - } - resp, err := httpClient.Do(req) - if err != nil { - log.Printf("unable to reach %q (%d/%d)", url, i+1, attempts) - time.Sleep(5 * time.Second) - continue - } - - if resp.StatusCode == http.StatusOK { - log.Println("console UI is up and running") - return nil - } - - log.Println("waiting UI console to be ready") - time.Sleep(5 * time.Second) - } - - return nil -} - -// todo: this is temporary -func openPortForwardForKubeConConsole() error { - - var wg sync.WaitGroup - wg.Add(8) - // argo workflows - go func() { - _, err := k8s.PortForward(false, "argo", "svc/argo-server", "2746:2746") - if err != nil { - log.Println("error opening Argo Workflows port forward") - } - wg.Done() - }() - // argocd - go func() { - _, err := k8s.PortForward(false, "argocd", "svc/argocd-server", "8080:80") - if err != nil { - log.Println("error opening ArgoCD port forward") - } - wg.Done() - }() - - // atlantis - go func() { - _, err := k8s.PortForward(false, "atlantis", "svc/atlantis", "4141:80") - if err != nil { - log.Println("error opening Atlantis port forward") - } - wg.Done() - }() - - // chartmuseum - go func() { - _, err := k8s.PortForward(false, "chartmuseum", "svc/chartmuseum", "8181:8080") - if err != nil { - log.Println("error opening Chartmuseum port forward") - } - wg.Done() - }() - - // vault - go func() { - _, err := k8s.PortForward(false, "vault", "svc/vault", "8200:8200") - if err != nil { - log.Println("error opening Vault port forward") - } - wg.Done() - }() - - // minio - go func() { - _, err := k8s.PortForward(false, "minio", "svc/minio", "9000:9000") - if err != nil { - log.Println("error opening Minio port forward") - } - wg.Done() - }() - - // minio console - go func() { - _, err := k8s.PortForward(false, "minio", "svc/minio-console", "9001:9001") - if err != nil { - log.Println("error opening Minio-console port forward") - } - wg.Done() - }() - - // Kubecon console ui - go func() { - _, err := k8s.PortForward(false, "kubefirst", "svc/kubefirst-console", "9094:80") - if err != nil { - log.Println("error opening Kubefirst-console port forward") - } - wg.Done() - }() - - wg.Wait() - - return nil -} diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index e6e178fb9..e0d85c44c 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -10,6 +10,7 @@ import ( "net/http" "os" "os/exec" + "sync" "time" "github.com/itchyny/gojq" @@ -454,3 +455,84 @@ func SetArgocdCreds(dryRun bool) { viper.Set("argocd.admin.username", "admin") viper.WriteConfig() } + +// todo: this is temporary +func OpenPortForwardForKubeConConsole() error { + + var wg sync.WaitGroup + wg.Add(8) + // argo workflows + go func() { + _, err := PortForward(false, "argo", "svc/argo-server", "2746:2746") + if err != nil { + log.Println("error opening Argo Workflows port forward") + } + wg.Done() + }() + // argocd + go func() { + _, err := PortForward(false, "argocd", "svc/argocd-server", "8080:80") + if err != nil { + log.Println("error opening ArgoCD port forward") + } + wg.Done() + }() + + // atlantis + go func() { + _, err := PortForward(false, "atlantis", "svc/atlantis", "4141:80") + if err != nil { + log.Println("error opening Atlantis port forward") + } + wg.Done() + }() + + // chartmuseum + go func() { + _, err := PortForward(false, "chartmuseum", "svc/chartmuseum", "8181:8080") + if err != nil { + log.Println("error opening Chartmuseum port forward") + } + wg.Done() + }() + + // vault + go func() { + _, err := PortForward(false, "vault", "svc/vault", "8200:8200") + if err != nil { + log.Println("error opening Vault port forward") + } + wg.Done() + }() + + // minio + go func() { + _, err := PortForward(false, "minio", "svc/minio", "9000:9000") + if err != nil { + log.Println("error opening Minio port forward") + } + wg.Done() + }() + + // minio console + go func() { + _, err := PortForward(false, "minio", "svc/minio-console", "9001:9001") + if err != nil { + log.Println("error opening Minio-console port forward") + } + wg.Done() + }() + + // Kubecon console ui + go func() { + _, err := PortForward(false, "kubefirst", "svc/kubefirst-console", "9094:80") + if err != nil { + log.Println("error opening Kubefirst-console port forward") + } + wg.Done() + }() + + wg.Wait() + + return nil +} diff --git a/internal/metaphor/metaphor.go b/internal/metaphor/metaphor.go index 05de8d87d..887eeffab 100644 --- a/internal/metaphor/metaphor.go +++ b/internal/metaphor/metaphor.go @@ -2,6 +2,7 @@ package metaphor import ( "fmt" + "github.com/kubefirst/kubefirst/pkg" "log" "os" @@ -96,3 +97,53 @@ func DeployMetaphorGithub(globalFlags flagset.GlobalFlags) error { viper.WriteConfig() return nil } + +// DeployMetaphorGithubLocal Deploy metaphor applications on github install +func DeployMetaphorGithubLocal(dryRun bool, gitHubOwner string, metaphorBranch string, templateTag string) error { + + if dryRun { + log.Printf("[#99] Dry-run mode, DeployMetaphorGithub skipped.") + return nil + } + + if viper.GetBool("github.metaphor-pushed") { + log.Println("github.metaphor-pushed already executed, skipped") + return nil + } + + config := configs.ReadConfig() + + tfEntrypoint := config.GitOpsRepoPath + "/terraform/github" + err := os.Rename(fmt.Sprintf("%s/%s", tfEntrypoint, "metaphor-repos.md"), fmt.Sprintf("%s/%s", tfEntrypoint, "metaphor-repos.tf")) + if err != nil { + log.Println("error renaming metaphor-repos.md to metaphor-repos.tf", err) + } + + gitClient.PushLocalRepoUpdates(pkg.GitHubHost, gitHubOwner, "gitops", "github") + terraform.InitApplyAutoApprove(dryRun, tfEntrypoint) + + repos := [3]string{"metaphor", "metaphor-go", "metaphor-frontend"} + for _, element := range repos { + log.Println("Processing Repo:", element) + repo.PrepareKubefirstTemplateRepo( + dryRun, + config, + gitHubOwner, + element, + metaphorBranch, + templateTag, + ) + log.Printf("clone and detokenization of %s-template repository complete", element) + + gitClient.PushLocalRepoToEmptyRemote(pkg.GitHubHost, gitHubOwner, element, "github") + + } + + viper.Set("github.metaphor-pushed", true) + err = viper.WriteConfig() + if err != nil { + return err + } + + return nil +} diff --git a/pkg/constants.go b/pkg/constants.go index a6d3afd8d..fc3582c64 100644 --- a/pkg/constants.go +++ b/pkg/constants.go @@ -9,6 +9,7 @@ const ( GitHubOAuthClientId = "2ced340927e0a6c49a45" CloudK3d = "k3d" GitHubProviderName = "github" + GitHubHost = "github.com" LocalClusterName = "kubefirst" ) diff --git a/pkg/helpers.go b/pkg/helpers.go index 4048e09c2..ccfad1adc 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "path/filepath" + "runtime" "strings" "time" @@ -494,3 +495,54 @@ func InformUser(message string, silentMode bool) { log.Println(message) progressPrinter.LogMessage(fmt.Sprintf("- %s", message)) } + +func OpenBrowser(url string) error { + var err error + + switch runtime.GOOS { + case "linux": + _, _, err = ExecShellReturnStrings("xdg-open", url) + case "windows": + _, _, err = ExecShellReturnStrings("rundll32", "url.dll,FileProtocolHandler", url) + case "darwin": + _, _, err = ExecShellReturnStrings("open", url) + default: + err = fmt.Errorf("unsupported platform") + } + if err != nil { + return err + } + + return nil +} + +// todo: this is temporary +func IsConsoleUIAvailable(url string) error { + attempts := 10 + httpClient := http.DefaultClient + for i := 0; i < attempts; i++ { + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + log.Printf("unable to reach %q (%d/%d)", url, i+1, attempts) + time.Sleep(5 * time.Second) + continue + } + resp, err := httpClient.Do(req) + if err != nil { + log.Printf("unable to reach %q (%d/%d)", url, i+1, attempts) + time.Sleep(5 * time.Second) + continue + } + + if resp.StatusCode == http.StatusOK { + log.Println("console UI is up and running") + return nil + } + + log.Println("waiting UI console to be ready") + time.Sleep(5 * time.Second) + } + + return nil +}