From 30da87ce2bf12364388761fc7edb304a8d608282 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 29 Jun 2022 13:37:38 +0000 Subject: [PATCH 001/107] adding go-cli Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- .gitignore | 6 +- Dockerfile | 137 ---- README.md | 72 ++ cmd/clean.go | 38 + cmd/destroy.go | 125 +++ cmd/globals.go | 27 + cmd/info.go | 46 ++ cmd/kubectl.go | 62 ++ cmd/nebulous.go | 41 + cmd/root.go | 82 ++ cmd/version.go | 24 + cmd/versionConstant.go | 5 + config.yaml | 24 + go.mod | 134 ++++ go.sum | 1151 +++++++++++++++++++++++++++ main.go | 40 + pkg/flare/aws.go | 31 + pkg/flare/envvars.go | 30 + pkg/flare/flareFile.go | 29 + pkg/flare/kubefirstDirectory.go | 30 + pkg/flare/progressTracker.go | 74 ++ pkg/flare/telemetry.go | 36 + pkg/ssh/ssh.go | 35 + scripts/nebulous/cleanup-cluster.sh | 41 - scripts/nebulous/destroy.sh | 118 --- scripts/nebulous/init.sh | 814 ------------------- scripts/nebulous/source-profile.sh | 24 - scripts/nebulous/wait-for-200.sh | 15 - terraform/main.tf | 1 + 29 files changed, 2142 insertions(+), 1150 deletions(-) delete mode 100644 Dockerfile create mode 100644 cmd/clean.go create mode 100644 cmd/destroy.go create mode 100644 cmd/globals.go create mode 100755 cmd/info.go create mode 100644 cmd/kubectl.go create mode 100644 cmd/nebulous.go create mode 100644 cmd/root.go create mode 100644 cmd/version.go create mode 100644 cmd/versionConstant.go create mode 100644 config.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 pkg/flare/aws.go create mode 100755 pkg/flare/envvars.go create mode 100755 pkg/flare/flareFile.go create mode 100755 pkg/flare/kubefirstDirectory.go create mode 100644 pkg/flare/progressTracker.go create mode 100644 pkg/flare/telemetry.go create mode 100644 pkg/ssh/ssh.go delete mode 100755 scripts/nebulous/cleanup-cluster.sh delete mode 100755 scripts/nebulous/destroy.sh delete mode 100755 scripts/nebulous/init.sh delete mode 100755 scripts/nebulous/source-profile.sh delete mode 100755 scripts/nebulous/wait-for-200.sh create mode 100644 terraform/main.tf diff --git a/.gitignore b/.gitignore index e323dda43..0dc59439d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,8 @@ kubeconfig_* */cypress/screenshots/ */cypress/videos/ .DS_Store -/git \ No newline at end of file +/git +bin +.vscode/settings.json +log +lint_log.txt diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index f54441d6a..000000000 --- a/Dockerfile +++ /dev/null @@ -1,137 +0,0 @@ -FROM ubuntu:focal - -ENV DEBIAN_FRONTEND noninteractive - -RUN apt-get update \ - && apt-get --no-install-recommends install -y \ - apt-transport-https \ - apt-utils \ - aufs-tools \ - automake \ - bash \ - bash-completion \ - bsdutils \ - build-essential \ - ca-certificates \ - coreutils \ - curl \ - findutils \ - git \ - gnupg-agent \ - gnupg \ - gnupg2 \ - golang-go \ - file \ - grep \ - groff \ - jq \ - less \ - libgtk2.0-0 \ - libgtk-3-0 \ - libgbm-dev \ - libnotify-dev \ - libgconf-2-4 \ - libssl-dev \ - libnss3 \ - libxss1 \ - libasound2 \ - libxtst6 \ - lsb-release \ - make \ - manpages-dev \ - openssh-client \ - openssl \ - postgresql \ - python3 \ - python3-pip \ - sed \ - software-properties-common \ - ssh \ - unzip \ - util-linux \ - vim \ - wget \ - xvfb \ - xauth \ - zip \ - && rm -rf /var/lib/apt/lists/* - -RUN add-apt-repository ppa:git-core/ppa -y \ - && apt-get update \ - && apt-get install git -y - -RUN pip3 install pyyaml semver --upgrade - -# install docker -RUN curl -fsSL https://get.docker.com -o get-docker.sh -RUN /bin/sh get-docker.sh - -# install aws cli v2 -RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \ - && unzip awscliv2.zip \ - && ./aws/install - -# Installation of NVM, NPM and packages -RUN mkdir /usr/local/nvm -ENV NVM_DIR /usr/local/nvm -ENV NODE_VERSION 14.15.1 -ENV NVM_INSTALL_PATH $NVM_DIR/versions/node/v$NODE_VERSION -RUN rm /bin/sh && ln -s /bin/bash /bin/sh -RUN curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.37.2/install.sh | bash -RUN source $NVM_DIR/nvm.sh \ - && nvm install $NODE_VERSION \ - && nvm alias default $NODE_VERSION \ - && nvm use default -ENV NODE_PATH $NVM_INSTALL_PATH/lib/node_modules -ENV PATH $NVM_INSTALL_PATH/bin:$PATH - -# install kubectl -ENV KUBECTL_VERSION v1.17.17 -ADD https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl /usr/local/bin/kubectl -RUN chmod +x /usr/local/bin/kubectl - -# install hashicorp terraform -ENV TF_VERSION 0.13.5 -RUN curl -LO https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip \ - && unzip terraform_${TF_VERSION}_linux_amd64.zip \ - && chmod +x terraform \ - && mv terraform /usr/local/bin/terraform \ - && rm -f terraform_${TF_VERSION}_linux_amd64.zip - -# install hashicorp vault -ENV VAULT_VERSION 1.6.2 -RUN curl -LO https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip \ - && unzip vault_${VAULT_VERSION}_linux_amd64.zip \ - && chmod +x vault \ - && mv vault /usr/local/bin/vault \ - && rm -f vault_${VAULT_VERSION}_linux_amd64.zip - -# install helm v3 -# DESIRED_VERSION is the helm version to install -ENV DESIRED_VERSION v3.5.0 -RUN mkdir -p $HOME/.helm && export HELM_HOME="$HOME/.helm" && curl -L https://git.io/get_helm.sh | /bin/bash -RUN helm plugin install https://github.com/chartmuseum/helm-push.git - -# install argocd -RUN ARGOCD_VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') \ -&& curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/$ARGOCD_VERSION/argocd-linux-amd64 \ -&& chmod +x /usr/local/bin/argocd \ -&& argocd version --short --client - -# install argo -RUN ARGO_VERSION="v3.1.11" \ -&& curl -sLO https://github.com/argoproj/argo-workflows/releases/download/$ARGO_VERSION/argo-linux-amd64.gz \ -&& gunzip argo-linux-amd64.gz \ -&& chmod +x argo-linux-amd64 \ -&& mv ./argo-linux-amd64 /usr/local/bin/argo \ -&& argo version --short - -ADD scripts/nebulous /scripts/nebulous -ADD gitops /gitops -ADD metaphor /metaphor -ADD images /images - -RUN apt-get update -RUN apt-get install dnsutils -y - -CMD [ "/bin/bash" ] diff --git a/README.md b/README.md index 724cdab2d..ebb3054d1 100644 --- a/README.md +++ b/README.md @@ -126,3 +126,75 @@ With that context in mind, once you've removed the manual things you may have ad ``` docker run -it --env-file=kubefirst.env -v $PWD/gitops:/gitops -v $PWD/metaphor:/metaphor -v $PWD/scripts:/scripts -v $PWD/git:/git --entrypoint /scripts/nebulous/destroy.sh nebulous:foo ``` + +### New README + +# Flare + +- [Flare](#flare) + - [Start](#start) + - [Start Environment variables](#start-environment-variables) + - [Start Actions](#start-actions) + - [Start Confirmation](#start-confirmation) + - [Destroy](#destroy) + - [Destroy Actions](#destroy-actions) + - [Notes:](#notes) + +## Start + +### Start Environment variables + +In order to start Kubefirst, the required environment variables are: + +| Variable | example | +|------------------|--------------------| +| AWS_PROFILE | default | +| AWS_REGION | us-east-1 | +| HOSTED_ZONE_NAME | mydomain.com | +| ADMIN_EMAIL | myemail@somewhere.com | + +### Start Actions + +```bash +touch ~/.flare +mkdir -p ~/.kubefirst +cd ~/git/kubefirst/gitlab/flare # change to your dir if different +go build -o bin/flare main.go +./bin/flare nebulous init --admin-email $ADMIN_EMAIL --cloud aws --hosted-zone-name $HOSTED_ZONE_NAME --region $AWS_REGION +./bin/flare nebulous create +``` + +### Start Confirmation + +```bash +aws eks update-kubeconfig --name kubefirst +kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d +kubectl -n argocd port-forward svc/argocd-server 8080:80 +``` + +## Destroy + +To destroy remote then local. + +These environment variables are expected: + +| Variable | example | +|------------------|-----------------------------------------------------------------------------------------------| +| AWS_PROFILE | default | +| AWS_REGION | us-east-1 | +| AWS_ACCOUNT_ID | 1xxxxxxxxxx4 | +| HOSTED_ZONE_NAME | mydomain.com | +| GITLAB_TOKEN | "xxxxx1-xx1x-x1xx-1" # replace with value from ~/.flare (only needed if you got to gitlab tf) | + + +### Destroy Actions +```bash +./bin/flare nebulous destroy +rm -rf ~/.kubefirst +rm ~/.flare +``` + +#### Notes: + +added gitlab.yaml to registry +pushing local to soft origin diff --git a/cmd/clean.go b/cmd/clean.go new file mode 100644 index 000000000..fc6aa3fe4 --- /dev/null +++ b/cmd/clean.go @@ -0,0 +1,38 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "fmt" + "log" + "os" + + "github.com/spf13/cobra" +) + +// cleanCmd represents the clean command +var cleanCmd = &cobra.Command{ + Use: "clean", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + log.Println("removing $HOME/.kubefirst and $HOME/.flare") + // todo ask for user input to verify? + os.RemoveAll(fmt.Sprintf("%s/.kubefirst", home)) + os.Remove(fmt.Sprintf("%s/.flare", home)) + log.Println("removed $HOME/.kubefirst and $HOME/.flare") + // todo log.Println("proceed to kubefirst create ") + log.Println("proceed to flare nebulous create ") + }, +} + +func init() { + nebulousCmd.AddCommand(cleanCmd) +} diff --git a/cmd/destroy.go b/cmd/destroy.go new file mode 100644 index 000000000..da56bf482 --- /dev/null +++ b/cmd/destroy.go @@ -0,0 +1,125 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "fmt" + "os" + "os/exec" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// destroyCmd represents the destroy command +var destroyCmd = &cobra.Command{ + Use: "destroy", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + // todo this needs to be removed when we are no longer in the starter account + os.Setenv("AWS_PROFILE", "starter") + + fmt.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") + // kubeconfig := os.Getenv("HOME") + "/.kube/config" + // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) + // argocdclientset, err := argocdclientset.NewForConfig(config) + // if err != nil { + // return nil, err + // } + + // todo should we git clone the gitops repo when destroy is run back to their local host to get the latest values of gitops ? + + os.Setenv("AWS_REGION", viper.GetString("aws.region")) + os.Setenv("AWS_ACCOUNT_ID", viper.GetString("aws.accountid")) + os.Setenv("HOSTED_ZONE_NAME", viper.GetString("aws.domainname")) + os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) + + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) + + skipGitlabTerraform, _ := cmd.Flags().GetBool("skip-gitlab-terraform") + //! terraform destroy gitlab + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) + err := os.Chdir(directory) + if err != nil { + fmt.Println("error changing dir: ", directory) + } + + os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) + + if !skipGitlabTerraform { + tfInitGitlabCmd := exec.Command(terraformPath, "init") + tfInitGitlabCmd.Stdout = os.Stdout + tfInitGitlabCmd.Stderr = os.Stderr + err = tfInitGitlabCmd.Run() + if err != nil { + fmt.Println("failed to call terraform init gitlab: ", err) + panic("failed to terraform init gitlab") + } + + tfDestroyGitlabCmd := exec.Command(terraformPath, "destroy", "-auto-approve") + tfDestroyGitlabCmd.Stdout = os.Stdout + tfDestroyGitlabCmd.Stderr = os.Stderr + err = tfDestroyGitlabCmd.Run() + if err != nil { + fmt.Println("failed to call terraform destroy gitlab: ", err) + panic("failed to terraform destroy gitlab") + } + + viper.Set("destroy.terraformdestroy.gitlab", true) + viper.WriteConfig() + } + + //! terraform destroy base + directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) + err = os.Chdir(directory) + if err != nil { + fmt.Println("error changing dir: ", directory) + } + + tfInitBaseCmd := exec.Command(terraformPath, "init") + tfInitBaseCmd.Stdout = os.Stdout + tfInitBaseCmd.Stderr = os.Stderr + err = tfInitBaseCmd.Run() + if err != nil { + fmt.Println("failed to call terraform init base: ", err) + } + + tfDestroyBaseCmd := exec.Command(terraformPath, "destroy", "-auto-approve") + tfDestroyBaseCmd.Stdout = os.Stdout + tfDestroyBaseCmd.Stderr = os.Stderr + err = tfDestroyBaseCmd.Run() + if err != nil { + fmt.Println("failed to call terraform destroy base: ", err) + panic("failed to terraform destroy base") + } + + viper.Set("destroy.terraformdestroy.base", true) + viper.WriteConfig() + }, +} + +func init() { + nebulousCmd.AddCommand(destroyCmd) + + destroyCmd.Flags().Bool("skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // destroyCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // destroyCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/globals.go b/cmd/globals.go new file mode 100644 index 000000000..6e6d01766 --- /dev/null +++ b/cmd/globals.go @@ -0,0 +1,27 @@ +package cmd + +import ( + "fmt" + "os" + "runtime" +) +//Common used strings by all commands +var home, kubectlClientPath, kubeconfigPath,localOs,localArchitecture,terraformPath,helmClientPath string +var dryrunMode bool + +//setGlobals for all common used properties +func setGlobals() { + tmphome, err := os.UserHomeDir() + home = tmphome + if(err != nil){ + fmt.Printf("Error Defining home - %s", err) + os.Exit(1) + } + localOs = runtime.GOOS + localArchitecture = runtime.GOARCH + kubectlClientPath = fmt.Sprintf("%s/.kubefirst/tools/kubectl", home) + kubeconfigPath = fmt.Sprintf("%s/.kubefirst/gitops/terraform/base/kubeconfig_kubefirst", home) + terraformPath = fmt.Sprintf("%s/.kubefirst/tools/terraform", home) + helmClientPath = fmt.Sprintf("%s/.kubefirst/tools/helm", home) + dryrunMode = false +} \ No newline at end of file diff --git a/cmd/info.go b/cmd/info.go new file mode 100755 index 000000000..a63cf0ca3 --- /dev/null +++ b/cmd/info.go @@ -0,0 +1,46 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "log" + "github.com/spf13/cobra" + "gitlab.kubefirst.io/kubefirst/flare/pkg/flare" +) + +// infoCmd represents the info command +var infoCmd = &cobra.Command{ + Use: "info", + Short: "Provide a general overview of host machine and cli", + Long: `Command used to allow a deeper inspection of the host machine + and cli version runnig and its current state. Tool recommended for troubleshooting + installations`, + Run: func(cmd *cobra.Command, args []string) { + log.Printf("flare-cli golang utility version: v%s", NebolousVersion) + log.Printf("OS type: %s", localOs) + log.Printf("Arch: %s", localArchitecture) + log.Printf("$HOME folder: %s", home) + log.Printf("kubectl used: %s", kubectlClientPath) + log.Printf("terraform used: %s", terraformPath) + log.Printf("Kubeconfig in use: %s", kubeconfigPath) + flare.CheckFlareFile(home) + flare.CheckKubefirstDir(home) + flare.CheckEnvironment() + }, +} + +func init() { + rootCmd.AddCommand(infoCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // infoCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // infoCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/kubectl.go b/cmd/kubectl.go new file mode 100644 index 000000000..856e9ef0c --- /dev/null +++ b/cmd/kubectl.go @@ -0,0 +1,62 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "context" + "encoding/json" + "fmt" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" +) + +var vaultRootToken string +var gitlabToolboxPodName string + +// API client for managing secrets & pods +var gitlabSecretClient coreV1Types.SecretInterface +var vaultSecretClient coreV1Types.SecretInterface +var argocdSecretClient coreV1Types.SecretInterface +var gitlabPodsClient coreV1Types.PodInterface + + +func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { + pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) + if err != nil { + fmt.Println(err) + } + + gitlabToolboxPodName = pods.Items[0].Name + + return gitlabToolboxPodName +} + +func getVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { + name := "vault-unseal-keys" + fmt.Printf("Reading secret %s\n", name) + secret, err := vaultSecretClient.Get(context.TODO(), name, metaV1.GetOptions{}) + + if err != nil { + panic(err.Error()) + } + + var jsonData map[string]interface{} + + for _, value := range secret.Data { + if err := json.Unmarshal(value, &jsonData); err != nil { + panic(err) + } + vaultRootToken = jsonData["root_token"].(string) + } + return vaultRootToken +} + +func getSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key string) string { + secret, err := k8sClient.Get(context.TODO(), secretName, metaV1.GetOptions{}) + if err != nil { + fmt.Println(fmt.Sprintf("error getting key: %s from secret: %s", key, secretName), err) + } + return string(secret.Data[key]) +} diff --git a/cmd/nebulous.go b/cmd/nebulous.go new file mode 100644 index 000000000..6449ab4ae --- /dev/null +++ b/cmd/nebulous.go @@ -0,0 +1,41 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// nebulousCmd represents the nebulous command +var nebulousCmd = &cobra.Command{ + Use: "nebulous", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("nebulous called") + + }, +} + +func init() { + rootCmd.AddCommand(nebulousCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // nebulousCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // nebulousCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 000000000..f0a09f4bd --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,82 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "os" + "errors" + "log" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var cfgFile string + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "flare", + Short: "A brief description of your application", + Long: `A longer description that spans multiple lines and likely contains +examples and usage of using your application. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + // Uncomment the following line if your bare application + // has an action associated with it: + Run: func(cmd *cobra.Command, args []string) { + log.Println(viper.Get("name")) //! print value coming from ~/.flare --> ~/.kubefirst + }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + setGlobals() + cobra.OnInitialize(initConfig) + + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.flare)") + + // 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") +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + + if cfgFile == "" { + cfgFile = home + "/.flare" + } + + if _, err := os.Stat(cfgFile); errors.Is(err, os.ErrNotExist) { + log.Printf("Config file not found, creating a blank one: %s \n", cfgFile) + err := os.WriteFile(cfgFile, []byte("createdBy: installer\n\n"), 0700) + if err != nil { + panic(err) + } + + } + viper.SetConfigFile(cfgFile) + viper.SetConfigType("yaml") + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + log.Println( "Using config file:", viper.ConfigFileUsed()) + } + +} diff --git a/cmd/version.go b/cmd/version.go new file mode 100644 index 000000000..b700bc60f --- /dev/null +++ b/cmd/version.go @@ -0,0 +1,24 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "log" + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(versionCmd) +} + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Print the version number for flare", + Long: `All software has versions. This is flare's`, + Run: func(cmd *cobra.Command, args []string) { + log.Printf("flare-cli golang utility version: v%s", NebolousVersion) + + }, +} diff --git a/cmd/versionConstant.go b/cmd/versionConstant.go new file mode 100644 index 000000000..581dcf4bf --- /dev/null +++ b/cmd/versionConstant.go @@ -0,0 +1,5 @@ +package cmd + +//This file may be updated/generated by CI/CD to always produce final and unique versions +//allowing easier to track source of bugs. +const NebolousVersion = "0.0.1" \ No newline at end of file diff --git a/config.yaml b/config.yaml new file mode 100644 index 000000000..03535e791 --- /dev/null +++ b/config.yaml @@ -0,0 +1,24 @@ +# The name of the server to show in the TUI. +name: Soft Serve + +# The host and port to display in the TUI. You may want to change this if your +# server is accessible from a different host and/or port that what it's +# actually listening on (for example, if it's behind a reverse proxy). +host: localhost +port: 23231 + +# Access level for anonymous users. Options are: admin-access, read-write, +# read-only, and no-access. +anon-access: read-write + +# You can grant read-only access to users without private keys. Any password +# will be accepted. +allow-keyless: true + +# Customize repo display in the menu. +repos: + - name: Home + repo: config + private: true + note: "Configuration and content repo for this server" + readme: README.md \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..f380180b4 --- /dev/null +++ b/go.mod @@ -0,0 +1,134 @@ +module gitlab.kubefirst.io/kubefirst/flare + +go 1.17 + +require ( + github.com/aws/aws-sdk-go v1.44.23 + github.com/aws/aws-sdk-go-v2/config v1.15.7 + github.com/aws/aws-sdk-go-v2/service/ecr v1.17.6 + github.com/aws/aws-sdk-go-v2/service/eks v1.21.1 + github.com/aws/aws-sdk-go-v2/service/kms v1.17.3 + github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5 + github.com/aws/aws-sdk-go-v2/service/sts v1.16.6 + github.com/cip8/autoname v1.0.0 + github.com/ghodss/yaml v1.0.0 + github.com/go-git/go-git/v5 v5.4.2 + github.com/google/uuid v1.1.2 + github.com/hashicorp/vault/api v1.6.0 + github.com/jedib0t/go-pretty/v6 v6.3.1 + github.com/spf13/cobra v1.4.0 + github.com/spf13/viper v1.11.0 + github.com/xanzy/go-gitlab v0.68.0 + golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 + k8s.io/api v0.24.2 + k8s.io/apimachinery v0.24.2 + k8s.io/client-go v0.24.2 + github.com/argoproj/argo-cd/v2 v2.0.5 +) + +replace k8s.io/client-go => k8s.io/client-go v0.22.1 + +replace k8s.io/apimachinery => k8s.io/apimachinery v0.22.1 + + +require ( + github.com/Microsoft/go-winio v0.5.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect + github.com/acomagu/bufpipe v1.0.3 // indirect + github.com/armon/go-metrics v0.3.10 // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.16.5 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.11.5 // indirect + github.com/aws/smithy-go v1.11.3 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emirpasic/gods v1.12.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/go-git/gcfg v1.5.0 // indirect + github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/go-logr/logr v1.2.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/googleapis/gnostic v0.5.5 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.2.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/go-retryablehttp v0.7.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/go-uuid v1.0.2 // indirect + github.com/hashicorp/go-version v1.2.0 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/vault/sdk v0.5.0 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.2.0 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/xanzy/ssh-agent v0.3.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect + golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac // indirect + google.golang.org/grpc v1.45.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.66.4 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + k8s.io/klog/v2 v2.60.1 // indirect + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect + sigs.k8s.io/yaml v1.2.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..9bd545c66 --- /dev/null +++ b/go.sum @@ -0,0 +1,1151 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= +github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.44.23 h1:oFvpKJk5qdprnCcuCWk2/CADdvfYtyduQ392bMXjlYI= +github.com/aws/aws-sdk-go v1.44.23/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go-v2 v1.16.4/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= +github.com/aws/aws-sdk-go-v2 v1.16.5 h1:Ah9h1TZD9E2S1LzHpViBO3Jz9FPL5+rmflmb8hXirtI= +github.com/aws/aws-sdk-go-v2 v1.16.5/go.mod h1:Wh7MEsmEApyL5hrWzpDkba4gwAPc5/piwLVLFnCxp48= +github.com/aws/aws-sdk-go-v2/config v1.15.7 h1:PrzhYjDpWnGSpjedmEapldQKPW4x8cCNzUI8XOho1CM= +github.com/aws/aws-sdk-go-v2/config v1.15.7/go.mod h1:exERlvqx1OoUHrxQpMgrmfSW0H6B1+r3xziZD3bBXRg= +github.com/aws/aws-sdk-go-v2/credentials v1.12.2 h1:tX4EHQFU4+O9at5QjnwIKb/Qgv7MbgbUNtqTRF0Vu2M= +github.com/aws/aws-sdk-go-v2/credentials v1.12.2/go.mod h1:/XWqDVuzclEKvzileqtD7/t+wIhOogv//6JFlKEe0Wc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.5 h1:YPxclBeE07HsLQE8vtjC8T2emcTjM9nzqsnDi2fv5UM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.5/go.mod h1:WAPnuhG5IQ/i6DETFl5NmX3kKqCzw7aau9NHAGcm4QE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.11/go.mod h1:tmUB6jakq5DFNcXsXOA/ZQ7/C8VnSKYkx58OI7Fh79g= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12 h1:Zt7DDk5V7SyQULUUwIKzsROtVzp/kVvcz15uQx/Tkow= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12/go.mod h1:Afj/U8svX6sJ77Q+FPWMzabJ9QjbwP32YlopgKALUpg= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.5/go.mod h1:fV1AaS2gFc1tM0RCb015FJ0pvWVUfJZANzjwoO4YakM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 h1:eeXdGVtXEe+2Jc49+/vAzna3FAQnUD4AagAw8tzbmfc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6/go.mod h1:FwpAKI+FBPIELJIdmQzlLtRe8LQSOreMcM2wBsPMvvc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.12 h1:j0VqrjtgsY1Bx27tD0ysay36/K4kFMWRp9K3ieO9nLU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.12/go.mod h1:00c7+ALdPh4YeEUPXJzyU0Yy01nPGOq2+9rUaz05z9g= +github.com/aws/aws-sdk-go-v2/service/ecr v1.17.6 h1:R9FxvsuknGAoKDJ1YRKwbgkTbedZZ++R7BwscG/6vRk= +github.com/aws/aws-sdk-go-v2/service/ecr v1.17.6/go.mod h1:+eCLloB5OdOr47npoEKlHGphSa72k44lXebO8I9LpKk= +github.com/aws/aws-sdk-go-v2/service/eks v1.21.1 h1:CYRfUZNq2krWxqLE1ntvy40uVT/I6X1PniNHrE/W3ps= +github.com/aws/aws-sdk-go-v2/service/eks v1.21.1/go.mod h1:kLodo8S0UEoVEd3mHTKtGnAhCT2uvCUM/Jjfr3og1yI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.5 h1:gRW1ZisKc93EWEORNJRvy/ZydF3o6xLSveJHdi1Oa0U= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.5/go.mod h1:ZbkttHXaVn3bBo/wpJbQGiiIWR90eTBUVBrEHUEQlho= +github.com/aws/aws-sdk-go-v2/service/kms v1.17.3 h1:M9bIvNNpbtvDTlZC5I38Kn2yuinJZ/9L+AM2Qom23zI= +github.com/aws/aws-sdk-go-v2/service/kms v1.17.3/go.mod h1:EKkrWWXwWYf8x3Nrm6Oix3zZP9NRBHqxw5buFGVBHA0= +github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5 h1:Vex6D07/CmahT0LIaiRk+j9xyVAsvQxBa8iv38p3Ajc= +github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5/go.mod h1:QZWV7sxHUg/qsPJcAtAI9JyLPKZ78weHmdILmYMCqEE= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.5 h1:TfJ/zuOYvHnxkvohSwAF3Ppn9KT/SrGZuOZHTPy8Guw= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.5/go.mod h1:TFVe6Rr2joVLsYQ1ABACXgOC6lXip/qpX2x5jWg/A9w= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.6 h1:aYToU0/iazkMY67/BYLt3r6/LT/mUtarLAF5mGof1Kg= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.6/go.mod h1:rP1rEOKAGZoXp4iGDxSXFvODAtXpm34Egf0lL0eshaQ= +github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= +github.com/aws/smithy-go v1.11.3 h1:DQixirEFM9IaKxX1olZ3ke3nvxRS2xMDteKIDWxozW8= +github.com/aws/smithy-go v1.11.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cip8/autoname v1.0.0 h1:rErvh6Y/VyE+aifgZWnxrwcu9f6mx1cZIuVGrJ9rRJs= +github.com/cip8/autoname v1.0.0/go.mod h1:D9lnEk3INEWNKmo8KmmhHyD2JuvZs5/87OhtuWhMDF8= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= +github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= +github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= +github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= +github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc= +github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 h1:MBgwAFPUbfuI0+tmDU/aeM1MARvdbqWmiieXIalKqDE= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/go-secure-stdlib/tlsutil v0.1.1/go.mod h1:l8slYwnJA26yBz+ErHpp2IRCLr0vuOMGBORIz4rRiAs= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hashicorp/vault/api v1.6.0 h1:B8UUYod1y1OoiGHq9GtpiqSnGOUEWHaA26AY8RQEDY4= +github.com/hashicorp/vault/api v1.6.0/go.mod h1:h1K70EO2DgnBaTz5IsL6D5ERsNt5Pce93ueVS2+t0Xc= +github.com/hashicorp/vault/sdk v0.5.0 h1:EED7p0OCU3OY5SAqJwSANofY1YKMytm+jDHDQ2EzGVQ= +github.com/hashicorp/vault/sdk v0.5.0/go.mod h1:UJZHlfwj7qUJG8g22CuxUgkdJouFrBNvBHCyx8XAPdo= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jedib0t/go-pretty/v6 v6.3.1 h1:aOXiD9oqiuLH8btPQW6SfgtQN5zwhyfzZls8a6sPJ/I= +github.com/jedib0t/go-pretty/v6 v6.3.1/go.mod h1:FMkOpgGD3EZ91cW8g/96RfxoV7bdeJyzXPYgz1L1ln0= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0= +github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= +github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.11.0 h1:7OX/1FS6n7jHD1zGrZTM7WtY13ZELRyosK4k93oPr44= +github.com/spf13/viper v1.11.0/go.mod h1:djo0X/bA5+tYVoCn+C7cAYJGcVn/qYLFTG8gdUsX7Zk= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/xanzy/go-gitlab v0.68.0 h1:b2iMQHgZ1V+NyRqLRJVv6RFfr4xnd/AASeS/PETYL0Y= +github.com/xanzy/go-gitlab v0.68.0/go.mod h1:o4yExCtdaqlM8YGdDJWuZoBmfxBsmA9TPEjs9mx1UO4= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= +go.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.2/go.mod h1:2D7ZejHVMIfog1221iLSYlQRzrtECw3kz4I4VAQm3qI= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= +golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac h1:qSNTkEN+L2mvWcLgJOR+8bdHX9rN/IdU3A1Ghpfb1Rg= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= +gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= +gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= +k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI= +k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= +k8s.io/apimachinery v0.22.1 h1:DTARnyzmdHMz7bFWFDDm22AM4pLWTQECMpRTFu2d2OM= +k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= +k8s.io/client-go v0.22.1 h1:jW0ZSHi8wW260FvcXHkIa0NLxFBQszTlhiAVsU5mopw= +k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= +k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/main.go b/main.go new file mode 100644 index 000000000..67a9ffe4b --- /dev/null +++ b/main.go @@ -0,0 +1,40 @@ +/* +Copyright © 2022 Kubefirst Inc. devops@kubefirst.com + +*/ +package main + +import ( + "gitlab.kubefirst.io/kubefirst/flare/cmd" + "log" + "os" + "time" + "fmt" +) + +func main() { + now := time.Now() + epoch := now.Unix() + logsdir := "log" + os.Mkdir(logsdir,0700) + logfile := fmt.Sprintf("./%s/log_%d.log",logsdir,epoch) + fmt.Printf("Result will be logged at: %s \n",logfile) + file, err := openLogFile(logfile) + defer file.Close() + log.SetOutput(file) + if err != nil { + log.Fatal(err) + } + log.SetPrefix("LOG: ") + log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) + + cmd.Execute() +} + +func openLogFile(path string) (*os.File, error) { + logFile, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) + if err != nil { + return nil, err + } + return logFile, nil +} \ No newline at end of file diff --git a/pkg/flare/aws.go b/pkg/flare/aws.go new file mode 100644 index 000000000..b70763d04 --- /dev/null +++ b/pkg/flare/aws.go @@ -0,0 +1,31 @@ +package flare + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/aws/aws-sdk-go/aws" +) + +func DescribeCluster() { + + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + fmt.Println("failed to load configuration, error:", err) + } + // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration + eksClient := eks.NewFromConfig(cfg, func(o *eks.Options) { + o.Region = "us-east-2" + }) + + cluster, err := eksClient.DescribeCluster(context.TODO(), &eks.DescribeClusterInput{ + Name: aws.String("kubefirst"), + }) + if err != nil { + fmt.Println("error describing cluster", err) + } + // todo base64 encoded data : *cluster.Cluster.CertificateAuthority.Data, + fmt.Println("cluster:", *cluster.Cluster.Arn, *cluster.Cluster.Endpoint) +} diff --git a/pkg/flare/envvars.go b/pkg/flare/envvars.go new file mode 100755 index 000000000..6507910d5 --- /dev/null +++ b/pkg/flare/envvars.go @@ -0,0 +1,30 @@ +package flare + +import ( + "log" + "os" + ) + +//Verify the state of the ".flare" file used to config provisioning. +// +// Output: +// $PATH/.flare +func CheckEnvironment() bool { + + if value := os.Getenv("AWS_REGION"); value == "" { + log.Printf("AWS_REGION env var not set.") + log.Printf("AWS_REGION is recommended for execution.") + } else { + log.Printf("AWS_REGION env var set: %s",value) + } + if value := os.Getenv("AWS_PROFILE"); value == "" { + log.Printf("AWS_PROFILE env var not set.") + log.Printf("AWS_PROFILE is recommended for execution.") + } else { + log.Printf("AWS_PROFILE env var set: %s",value) + } + + + + return true +} \ No newline at end of file diff --git a/pkg/flare/flareFile.go b/pkg/flare/flareFile.go new file mode 100755 index 000000000..b45054731 --- /dev/null +++ b/pkg/flare/flareFile.go @@ -0,0 +1,29 @@ +package flare + +import ( + "log" + "fmt" + "os" + "errors" + ) + +//Verify the state of the ".flare" file used to config provisioning. +// +// Output: +// $PATH/.flare +func CheckFlareFile(home string) string { + flareFile := fmt.Sprintf("%s/.flare", home) + if _, err := os.Stat(flareFile); err == nil { + // path/to/whatever exists + log.Printf("\".flare\" file found: %s", flareFile) + } else if errors.Is(err, os.ErrNotExist) { + // path/to/whatever does *not* exist + log.Printf("\".flare\" file not found: %s", flareFile) + log.Printf(" \".flare\" is needed to guide installation process" ) + } else { + // Schrodinger: file may or may not exist. See err for details. + // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence + log.Printf("Unable to check is \".flare\" if file exists" ) + } + return flareFile +} \ No newline at end of file diff --git a/pkg/flare/kubefirstDirectory.go b/pkg/flare/kubefirstDirectory.go new file mode 100755 index 000000000..9ed513fa5 --- /dev/null +++ b/pkg/flare/kubefirstDirectory.go @@ -0,0 +1,30 @@ +package flare + +import ( + "log" + "fmt" + "os" + "errors" + ) + + +//Verify the state of the kubefirst directory +// +// Output: +// $PATH/.kubefirst +func CheckKubefirstDir(home string) string { + k1sDir := fmt.Sprintf("%s/.kubefirst", home) + if _, err := os.Stat(k1sDir); err == nil { + // path/to/whatever exists + log.Printf("\".kubefirst\" file found: %s", k1sDir) + log.Printf(" \".kubefirst\" will be generated by installation process, if exist means a installation may already be executed" ) + } else if errors.Is(err, os.ErrNotExist) { + // path/to/whatever does *not* exist + log.Printf("\".kubefirst\" file not found: %s", k1sDir) + } else { + // Schrodinger: file may or may not exist. See err for details. + // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence + log.Printf("Unable to check is \".kubefirst\" if file exists" ) + } + return k1sDir +} \ No newline at end of file diff --git a/pkg/flare/progressTracker.go b/pkg/flare/progressTracker.go new file mode 100644 index 000000000..bb94065da --- /dev/null +++ b/pkg/flare/progressTracker.go @@ -0,0 +1,74 @@ +package flare + +import ( + "github.com/jedib0t/go-pretty/v6/progress" + "github.com/jedib0t/go-pretty/v6/text" + "flag" + "fmt" + "time" +) + +type ActionTracker struct { + //Message string + //Total int64 + //Increment int64 + Tracker *progress.Tracker +} + +var ( + flagAutoStop = flag.Bool("auto-stop", false, "Auto-stop rendering?") + flagHideETA = flag.Bool("hide-eta", false, "Hide the ETA?") + flagHideETAOverall = flag.Bool("hide-eta-overall", false, "Hide the ETA in the overall tracker?") + flagHideOverallTracker = flag.Bool("hide-overall", false, "Hide the Overall Tracker?") + flagHidePercentage = flag.Bool("hide-percentage", false, "Hide the progress percent?") + flagHideTime = flag.Bool("hide-time", false, "Hide the time taken?") + flagHideValue = flag.Bool("hide-value", false, "Hide the tracker value?") +// flagNumTrackers = flag.Int("num-trackers", 12, "Number of Trackers") + flagRandomFail = flag.Bool("rnd-fail", false, "Introduce random failures in tracking") + flagRandomLogs = flag.Bool("rnd-logs", false, "Output random logs in the middle of tracking") + + messageColors = []text.Color{ + text.FgRed, + text.FgGreen, + text.FgYellow, + text.FgBlue, + text.FgMagenta, + text.FgCyan, + text.FgWhite, + } +) +var pw progress.Writer + +func SetupProgress(numTrackers int){ + flagNumTrackers := flag.Int("num-trackers", numTrackers, "Number of Trackers") + flag.Parse() + fmt.Printf("Init actions: %d expected tasks ...\n\n", *flagNumTrackers) + // instantiate a Progress Writer and set up the options + pw = progress.NewWriter() + pw.SetAutoStop(*flagAutoStop) + pw.SetTrackerLength(30) + pw.SetMessageWidth(29) + pw.SetNumTrackersExpected(*flagNumTrackers) + pw.SetSortBy(progress.SortByPercentDsc) + pw.SetStyle(progress.StyleDefault) + pw.SetTrackerPosition(progress.PositionRight) + pw.SetUpdateFrequency(time.Millisecond * 100) + pw.Style().Colors = progress.StyleColorsExample + pw.Style().Options.PercentFormat = "%4.1f%%" + pw.Style().Visibility.ETA = !*flagHideETA + pw.Style().Visibility.ETAOverall = !*flagHideETAOverall + pw.Style().Visibility.Percentage = !*flagHidePercentage + pw.Style().Visibility.Time = !*flagHideTime + pw.Style().Visibility.TrackerOverall = !*flagHideOverallTracker + pw.Style().Visibility.Value = !*flagHideValue + go pw.Render() + +} + +func CreateTracker(title string, total int64) *progress.Tracker{ + units := &progress.UnitsDefault + message := title + tracker := progress.Tracker{Message: message, Total: total, Units: *units} + pw.AppendTracker(&tracker) + return &tracker +} \ No newline at end of file diff --git a/pkg/flare/telemetry.go b/pkg/flare/telemetry.go new file mode 100644 index 000000000..c1b0e0b36 --- /dev/null +++ b/pkg/flare/telemetry.go @@ -0,0 +1,36 @@ +package flare + +import ( + "fmt" + "io/ioutil" + "net/http" + "strings" +) + +func SendTelemetry(domain, metricName string) { + + url := "https://metaphor-go-production.kubefirst.io/telemetry" + method := "POST" + + payload := strings.NewReader(fmt.Sprintf(`{"domain": "%s","name": "%s"}`, domain, metricName)) + + client := &http.Client{} + req, err := http.NewRequest(method, url, payload) + + if err != nil { + fmt.Println(err) + } + + req.Header.Add("Content-Type", "application/json") + // TODO need to add authentication or a header of some sort? + // req.Header.Add("auth?", os.Getenv("K1_KEY")) + + res, err := client.Do(req) + if err != nil { + fmt.Println("error") + } + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + + fmt.Println(string(body)) +} diff --git a/pkg/ssh/ssh.go b/pkg/ssh/ssh.go new file mode 100644 index 000000000..20a360eff --- /dev/null +++ b/pkg/ssh/ssh.go @@ -0,0 +1,35 @@ +package ssh + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + + "golang.org/x/crypto/ssh" +) + +func marshalRSAPrivate(priv *rsa.PrivateKey) string { + return string(pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv), + })) +} + +func GenerateKey() (string, string, error) { + reader := rand.Reader + bitSize := 2048 + + key, err := rsa.GenerateKey(reader, bitSize) + if err != nil { + return "", "", err + } + + pub, err := ssh.NewPublicKey(key.Public()) + if err != nil { + return "", "", err + } + pubKeyStr := string(ssh.MarshalAuthorizedKey(pub)) + privKeyStr := marshalRSAPrivate(key) + + return pubKeyStr, privKeyStr, nil +} diff --git a/scripts/nebulous/cleanup-cluster.sh b/scripts/nebulous/cleanup-cluster.sh deleted file mode 100755 index a824a97f0..000000000 --- a/scripts/nebulous/cleanup-cluster.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -### -# usage: ./scripts/nebulous/cleanup-cluster.sh -### - -echo -echo "uninstalling the helm deployments" -helm -n external-dns uninstall external-dns -helm -n gitlab-runner uninstall gitlab-runner - -echo -echo -echo "IMPORTANT:" -echo " *NOTE* please delete any kind: Service you've created in the cluster prior to destroying" -echo " with terraform. the service type: LoadBalancer will be left in your cloud because the ACM" -echo " certificate will still be in use. this could lead to abandoned resources in your aws account" -echo -echo -sleep 8 - -echo -echo "deleting development metaphor resources" -kubectl -n development delete service/metaphor deploy/metaphor secrets/metaphor-secrets - -echo -echo "deleting staging metaphor resources" -kubectl -n staging delete service/metaphor deploy/metaphor secrets/metaphor-secrets - -echo -echo "deleting production metaphor resources" -kubectl -n production delete service/metaphor deploy/metaphor secrets/metaphor-secrets - -echo -echo "deleting cluster namespaces" -kubectl delete namespaces development staging production gitlab-runner external-dns - -echo -echo -echo "successfully unintalled metaphor and blew up the namespaces BOOM!" -echo diff --git a/scripts/nebulous/destroy.sh b/scripts/nebulous/destroy.sh deleted file mode 100755 index 8a8bbcef3..000000000 --- a/scripts/nebulous/destroy.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env bash -### -# usage: ./scripts/nebulous/destroy.sh -### - -set -e - -source ~/.profile - -echo -echo -echo -echo -echo -echo -echo ' __ ___. _____.__ __ ' -echo '| | ____ _\_ |__ _____/ ____\__|______ _______/ |_ ' -echo '| |/ / | \ __ \_/ __ \ __\| \_ __ \/ ___/\ __\' -echo '| <| | / \_\ \ ___/| | | || | \/\___ \ | | ' -echo '|__|_ \____/|___ /\___ >__| |__||__| /____ > |__| ' -echo ' \/ \/ \/ \/ ' - - -echo -echo -echo " https://docs.kubefirst.com" -echo -echo -echo -echo - -sleep 12 - -if [ -z "$BUCKET_RAND" ] -then - echo - echo '########################################' - echo '# ERROR #' - echo '########################################' - echo - echo 'you cannot run nebulous destroy without first setting your BUCKET_RAND value in your kubefirst.env file.' - echo 'please set and uncomment your BUCKET_RAND value in kubefirst.env and run the destroy operation again.' - exit 1; -fi - -echo "establishing kubectl config" -K8S_CLUSTER_NAME=kubefirst -aws eks update-kubeconfig --region $AWS_DEFAULT_REGION --name $K8S_CLUSTER_NAME -chmod 0600 ~/.kube/config - -echo "pulling secrets from secret/atlantis" -export VAULT_TOKEN=$(kubectl -n vault get secret vault-unseal-keys -ojson | jq -r '.data."cluster-keys.json"' | base64 -d | jq -r .root_token) -export VAULT_ADDR="https://vault.${AWS_HOSTED_ZONE_NAME}" -vault login $VAULT_TOKEN -$(echo $(vault kv get -format=json secret/atlantis | jq -r .data.data) | jq -r 'keys[] as $k | "export \($k)=\(.[$k])"') - -if [ -z "$SKIP_ARGOCD_APPLY" ] -then - echo "forcefully destroying argo, gitlab, and chartmuseum buckets (leaving state store intact)" - aws s3 rb s3://k1-argo-artifacts-$BUCKET_RAND --force - aws s3 rb s3://k1-gitlab-backup-$BUCKET_RAND --force - aws s3 rb s3://k1-chartmuseum-$BUCKET_RAND --force - - echo "##############################################################" - echo "#" - echo "# RETAIN SECRETS!! MAY BE NEEDED AFTER VAULT IS DESTROYED:" - echo "# (you can safely discard after destroy is complete)" - echo "#" - echo $(vault kv get -format=json secret/atlantis | jq -r .data.data) | jq -r 'keys[] as $k | "export \($k)=\(.[$k])"' - echo "##############################################################" - - cd /git/gitops/terraform/argocd - echo "destroying argocd terraform" - terraform init - terraform destroy -target module.argocd_registry -target module.argocd_repos -auto-approve - echo "argocd terraform destroy complete" - - echo "waiting 240 seconds for app registration destruction" - sleep 30 - echo "waiting 210 more seconds for app registration destruction" - sleep 30 - echo "waiting 180 more seconds for app registration destruction" - sleep 30 - echo "waiting 150 more seconds for app registration destruction" - sleep 30 - echo "waiting 120 more seconds for app registration destruction" - sleep 30 - echo "waiting 90 more seconds for app registration destruction" - sleep 30 - echo "waiting 60 more seconds for app registration destruction" - sleep 30 - echo "waiting 30 more seconds for app registration destruction" - sleep 30 -fi - - -if [ -z "$SKIP_GITLAB_APPLY" ] -then - cd /git/gitops/terraform/gitlab - echo "destroying gitlab terraform" - terraform init - terraform destroy -auto-approve - echo "gitlab terraform destroy complete" -fi - - -if [ -z "$SKIP_BASE_APPLY" ] -then - cd /git/gitops/terraform/base - echo "destroying base terraform" - terraform init - terraform destroy -auto-approve - echo "base terraform destroy complete" -fi - - -echo "teardown operation complete" - diff --git a/scripts/nebulous/init.sh b/scripts/nebulous/init.sh deleted file mode 100755 index 49eb98860..000000000 --- a/scripts/nebulous/init.sh +++ /dev/null @@ -1,814 +0,0 @@ -#!/usr/bin/env bash - -set -e - -source ~/.profile - -echo -echo -echo -echo -echo -echo -echo ' __ ___. _____.__ __ ' -echo '| | ____ _\_ |__ _____/ ____\__|______ _______/ |_ ' -echo '| |/ / | \ __ \_/ __ \ __\| \_ __ \/ ___/\ __\' -echo '| <| | / \_\ \ ___/| | | || | \/\___ \ | | ' -echo '|__|_ \____/|___ /\___ >__| |__||__| /____ > |__| ' -echo ' \/ \/ \/ \/ ' - - -echo -echo -echo " welcome to the kubefirst/nebulous installation. the install time is about" -echo " 40 mins to provision your new infrastructure. most of this time is waiting" -echo " on infra to provision and dns to propagate. while you're waiting check out" -echo " our docs to familiarize yourself with what's ahead." -echo -echo " https://docs.kubefirst.com" -echo -echo -echo -echo - -sleep 12 - -echo "executing source-profile.sh" -source /scripts/nebulous/source-profile.sh - -# conditional configuration setup -if [ ! -f /git/gitops/terraform/base/terraform-ssh-key ] -then - echo "creating ssh key pair" - ssh-keygen -o -t rsa -b 4096 -C "${EMAIL_ADDRESS}" -f $HOME/.ssh/id_rsa -q -N "" > /dev/null - echo "copying ssh keys to /gitops/terraform/base/terraform-ssh-key*" - cp ~/.ssh/id_rsa /gitops/terraform/base/terraform-ssh-key - cp ~/.ssh/id_rsa.pub /gitops/terraform/base/terraform-ssh-key.pub - sleep 2 -else - echo "reusing existing ssh key pair" -fi - -if [ -z "$BUCKET_RAND" ]; then - BUCKET_RAND=$(openssl rand -hex 3) - echo - echo "############################################################" - echo "established BUCKET_RAND suffix: ${BUCKET_RAND}" - echo "set this in your kubefirst.env file for restarts/teardown" - echo "############################################################" - echo -else - echo "BUCKET_RAND seeded with specified value $BUCKET_RAND, skipping state bucket creation" - SKIP_STATE_BUCKET_CREATION="true" -fi - -export TF_STATE_BUCKET=k1-state-store-$BUCKET_RAND -export ARGO_ARTIFACT_BUCKET=k1-argo-artifacts-$BUCKET_RAND -export GITLAB_BACKUP_BUCKET=k1-gitlab-backup-$BUCKET_RAND -export CHARTMUSEUM_BUCKET=k1-chartmuseum-$BUCKET_RAND - - -if [ -z "$SKIP_STATE_BUCKET_CREATION" ] -then - echo "creating bucket $TF_STATE_BUCKET" - # TODO: --versioning-configuration Status=Enabled - if [[ "$AWS_DEFAULT_REGION" == "us-east-1" ]]; then - aws s3api create-bucket --bucket $TF_STATE_BUCKET --region $AWS_DEFAULT_REGION | jq -r .Location | cut -d/ -f2 - else - aws s3api create-bucket --bucket $TF_STATE_BUCKET --region $AWS_DEFAULT_REGION --create-bucket-configuration LocationConstraint=$AWS_DEFAULT_REGION | jq -r .Location | cut -d/ -f3 | cut -d. -f1 - fi - echo "enabling bucket versioning" - aws s3api put-bucket-versioning --bucket $TF_STATE_BUCKET --region $AWS_DEFAULT_REGION --versioning-configuration Status=Enabled -else - echo "reusing bucket name ${TF_STATE_BUCKET}" -fi - - -# setup environment variables -export AWS_HOSTED_ZONE_ID=$(aws route53 list-hosted-zones-by-name | jq --arg name "${AWS_HOSTED_ZONE_NAME}." -r '.HostedZones | .[] | select(.Name=="\($name)") | .Id') -export EMAIL_DOMAIN=$(echo $EMAIL_ADDRESS | cut -d"@" -f2) -export AWS_ACCOUNT_ID=$(aws sts get-caller-identity | jq -r .Account) -export IAM_USER_ARN=$(aws sts get-caller-identity | jq -r .Arn) -export GITLAB_URL_PREFIX=gitlab -export GITLAB_URL="${GITLAB_URL_PREFIX}.${AWS_HOSTED_ZONE_NAME}" -export GITLAB_ROOT_USER=root -export GITLAB_BASE_URL=https://gitlab.${AWS_HOSTED_ZONE_NAME} -export K8S_CLUSTER_NAME=kubefirst - -#* terraform separation: all these values should come from pre-determined env's -export TF_VAR_aws_account_id=$AWS_ACCOUNT_ID -export TF_VAR_aws_region=$AWS_DEFAULT_REGION -export TF_VAR_hosted_zone_name=$AWS_HOSTED_ZONE_NAME -export TF_VAR_hosted_zone_id=$AWS_HOSTED_ZONE_ID -export TF_VAR_gitlab_url=$GITLAB_URL -export TF_VAR_email_domain=$EMAIL_DOMAIN -export TF_VAR_iam_user_arn=$IAM_USER_ARN -export TF_VAR_gitlab_bot_root_password=$GITLAB_BOT_ROOT_PASSWORD -export TF_VAR_aws_access_key_id=$AWS_ACCESS_KEY_ID -export TF_VAR_aws_secret_access_key=$AWS_SECRET_ACCESS_KEY -export TF_VAR_email_address=$EMAIL_ADDRESS -export TF_VAR_vault_redirect_uris="[\"https://vault.${AWS_HOSTED_ZONE_NAME}/ui/vault/auth/oidc/oidc/callback\",\"http://localhost:8200/ui/vault/auth/oidc/oidc/callback\",\"http://localhost:8250/oidc/callback\",\"https://vault.${AWS_HOSTED_ZONE_NAME}:8250/oidc/callback\"]" -export TF_VAR_argo_redirect_uris="[\"https://argo.${AWS_HOSTED_ZONE_NAME}/oauth2/callback\"]" -export TF_VAR_argocd_redirect_uris="[\"https://argocd.${AWS_HOSTED_ZONE_NAME}/auth/callback\",\"https://argocd.${AWS_HOSTED_ZONE_NAME}/applications\"]" -export TF_VAR_gitlab_redirect_uris="[\"https://gitlab.${AWS_HOSTED_ZONE_NAME}\"]" - - -export TF_VAR_keycloak_password="" -export TF_VAR_atlantis_gitlab_webhook_secret=$ATLANTIS_GITLAB_WEBHOOK_SECRET #TODO: created empty - wire it in -export TF_VAR_keycloak_admin_password="" -export TF_VAR_keycloak_vault_oidc_client_secret=$TF_VAR_keycloak_vault_oidc_client_secret - - -# check for liveness of the hosted zone -if [ -z "$SKIP_HZ_CHECK" ]; then - HZ_LIVENESS_FAIL_COUNT=0 - HZ_IS_LIVE=0 - HZ_LIVENESS_URL=livenesstest.$AWS_HOSTED_ZONE_NAME - HZ_LIVENESS_JSON="{\"Comment\":\"CREATE sanity check record \",\"Changes\":[{\"Action\":\"CREATE\",\"ResourceRecordSet\":{\"Name\":\"$HZ_LIVENESS_URL\",\"Type\":\"A\",\"TTL\":300,\"ResourceRecords\":[{\"Value\":\"4.4.4.4\"}]}}]}" - echo "Creating $HZ_LIVENESS_URL record for sanity check" - HZ_RECORD_STATUS=$(aws route53 change-resource-record-sets --hosted-zone-id $AWS_HOSTED_ZONE_ID --change-batch "$HZ_LIVENESS_JSON" | jq -r .ChangeInfo.Status) - - while [[ $HZ_RECORD_STATUS == 'PENDING' && $HZ_LIVENESS_FAIL_COUNT -lt 8 && $HZ_IS_LIVE -eq 0 ]]; - do - echo "checking hosted zone configuration with validation of livenesstest.$AWS_HOSTED_ZONE_NAME" - HZ_LOOKUP_RESULT=$(nslookup "$HZ_LIVENESS_URL" 8.8.8.8 | awk -F':' '/^Address: / { matched = 1 } matched { print $2}' | xargs) - if [[ "$HZ_LOOKUP_RESULT" ]]; then - HZ_IS_LIVE=1 - echo "Sanity check passed" - else - HZ_LIVENESS_FAIL_COUNT=$((HZ_LIVENESS_FAIL_COUNT+1)) - echo "Sanity check url, $HZ_LIVENESS_URL, is not ready yet. Sleeping for 30 seconds" - sleep 30 - fi - done - - echo "Deleting $HZ_LIVENESS_URL record" - aws route53 change-resource-record-sets --hosted-zone-id $AWS_HOSTED_ZONE_ID --change-batch "$( echo "${HZ_LIVENESS_JSON//CREATE/DELETE}" )" > /dev/null - - if [[ $HZ_IS_LIVE -eq 0 ]]; then - echo "Error creating an A record in the provided hosted zone! we can't go on, check your zone, credentials, region, etc and try again" - exit 1 - fi -fi - -if [ -z "$SKIP_DETOKENIZATION" ]; then - # detokenize - export LC_CTYPE=C; - export LANG=C; - echo "copying constructed gitops repo content into /git/gitops directory" - mkdir -p /git - cd /git - cp -a /gitops/. /git/gitops/ - cp -a /metaphor/. /git/metaphor/ - cd /git/ - - # NOTE: this section represents values that need not be secrets and can be directly hardcoded in the - # clients' gitops repos. DO NOT handle secrets in this fashion - echo "replacing TF_STATE_BUCKET token with value ${TF_STATE_BUCKET} (1 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${TF_STATE_BUCKET}|g" - echo "replacing ARGO_ARTIFACT_BUCKET token with value ${ARGO_ARTIFACT_BUCKET} (2 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${ARGO_ARTIFACT_BUCKET}|g" - echo "replacing GITLAB_BACKUP_BUCKET token with value ${GITLAB_BACKUP_BUCKET} (3 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${GITLAB_BACKUP_BUCKET}|g" - echo "replacing CHARTMUSEUM_BUCKET token with value ${CHARTMUSEUM_BUCKET} (4 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${CHARTMUSEUM_BUCKET}|g" - echo "replacing AWS_ACCESS_KEY_ID token with value ${AWS_ACCESS_KEY_ID} (5 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${AWS_ACCESS_KEY_ID}|g" - echo "replacing AWS_HOSTED_ZONE_ID token with value ${AWS_HOSTED_ZONE_ID} (6 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${AWS_HOSTED_ZONE_ID}|g" - echo "replacing AWS_HOSTED_ZONE_NAME token with value ${AWS_HOSTED_ZONE_NAME} (7 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${AWS_HOSTED_ZONE_NAME}|g" - echo "replacing AWS_DEFAULT_REGION token with value ${AWS_DEFAULT_REGION} (8 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${AWS_DEFAULT_REGION}|g" - echo "replacing EMAIL_ADDRESS token with value ${EMAIL_ADDRESS} (9 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${EMAIL_ADDRESS}|g" - echo "replacing AWS_ACCOUNT_ID token with value ${AWS_ACCOUNT_ID} (10 of 10)" - find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s||${AWS_ACCOUNT_ID}|g" -fi - -# apply base terraform -cd /git/gitops/terraform/base -if [ -z "$SKIP_BASE_APPLY" ] -then - echo - echo '########################################' - echo '#' - echo '# BASE TERRAFORM' - echo '#' - echo '########################################' - echo - - echo "applying bootstrap terraform" - terraform init - terraform apply -auto-approve - # terraform destroy -auto-approve; exit 1; # hack - - KMS_KEY_ID=$(terraform output -json | jq -r '.vault_unseal_kms_key.value') - echo "KMS_KEY_ID collected: $KMS_KEY_ID" - echo "bootstrap terraform complete" - echo "replacing KMS_KEY_ID token with value ${KMS_KEY_ID}" - - cd /git/gitops - find . -type f -not -path '*/cypress/*' -exec sed -i "s||${KMS_KEY_ID}|g" {} + - -else - echo "skipping bootstrap terraform because SKIP_BASE_APPLY is set" -fi - -echo "getting ~/kube/config for eks access" -aws eks update-kubeconfig --region $AWS_DEFAULT_REGION --name $K8S_CLUSTER_NAME -cat ~/.kube/config -chmod 0600 ~/.kube/config - - - - - -#! gitlab -if [ -z "$SKIP_GITLAB_RECONFIG" ] -then - echo - echo '########################################' - echo '#' - echo '# GITLAB RECONFIG' - echo '#' - echo '########################################' - echo - - # reconfigure gitlab server - echo - echo "testing gitlab for connectivity" - echo - /scripts/nebulous/wait-for-200.sh "https://${GITLAB_URL}/help" - echo - echo "gitlab is ready, executing cypress" - echo - - export CYPRESS_BASE_URL="https://${GITLAB_URL}" - export CYPRESS_gitlab_bot_username_before=$GITLAB_ROOT_USER - export CYPRESS_gitlab_bot_password=$GITLAB_BOT_ROOT_PASSWORD - cd /git/gitops/terraform/cypress - npm ci - - $(npm bin)/cypress run - - - echo - echo " IMPORTANT:" - echo " THIS IS THE ROOT PASSWORD FOR YOUR GITLAB INSTANCE" - echo " DO NOT LOSE THIS VALUE" - echo - echo " username: root" - echo " password: ${CYPRESS_gitlab_bot_password}" - echo - echo " GitLab URL: https://${GITLAB_URL}/kubefirst" - echo - echo - echo - echo - sleep 18 -fi - - -export RUNNER_REGISTRATION_TOKEN=$(cat /git/gitops/terraform/.gitlab-runner-registration-token) -export GITLAB_TOKEN=$(cat /git/gitops/terraform/.gitlab-bot-access-token) -export TF_VAR_gitlab_token=$GITLAB_TOKEN -export TF_VAR_atlantis_gitlab_token=$GITLAB_TOKEN - -# apply terraform -if [ -z "$SKIP_GITLAB_APPLY" ] -then - echo - echo '########################################' - echo '#' - echo '# GITLAB TERRAFORM' - echo '#' - echo '########################################' - echo - - cd /git/gitops/terraform/gitlab - echo "applying gitlab terraform" - terraform init - terraform apply -auto-approve - # terraform destroy -auto-approve; exit 1 # TODO: hack - echo "gitlab terraform complete" - - cd /git/gitops - - echo "configuring git client" - git config --global user.name "Administrator" - git config --global user.email "${EMAIL_ADDRESS}" - - echo "initing gitops repo, committing, and pushing to the new gitlab origin" - git init --initial-branch=main - git remote add origin https://root:$GITLAB_TOKEN@gitlab.${AWS_HOSTED_ZONE_NAME}/kubefirst/gitops.git > /dev/null - git add . - git commit -m "initial kubefirst commit" - git push -u origin main - echo "gitops repo established" - - cd /git/metaphor - - echo "configuring git client" - git config --global user.name "Administrator" - git config --global user.email "${EMAIL_ADDRESS}" - - echo "initing metaphor repo, committing, and pushing to the new gitlab origin" - git init --initial-branch=main - git remote add origin https://root:$GITLAB_TOKEN@gitlab.${AWS_HOSTED_ZONE_NAME}/kubefirst/metaphor.git > /dev/null - git add . - git commit -m "initial kubefirst commit" - git push -u origin main - echo "metaphor repo established" - - echo "updating kubefirst group image" - export KUBEFIRST_GROUP_ID=$(curl -s --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" "https://gitlab.${AWS_HOSTED_ZONE_NAME}/api/v4/groups" | jq -r '.[] | select(.name == "kubefirst").id') - curl --request PUT --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" "https://gitlab.${AWS_HOSTED_ZONE_NAME}/api/v4/groups/${KUBEFIRST_GROUP_ID}" --form "avatar=@/images/kubefirst.png" - echo "group image update complete" -else - echo "skipping gitlab terraform because SKIP_GITLAB_APPLY is set" -fi - -echo -echo '########################################' -echo '#' -echo '# ARGOCD TERRAFORM' -echo '#' -echo '########################################' -echo - -echo "creating argocd in kubefirst cluster" -kubectl create namespace external-secrets-operator --dry-run=true -o yaml | kubectl apply -f - -kubectl create namespace argo --dry-run=true -o yaml | kubectl apply -f - -kubectl create namespace argocd --dry-run -oyaml | kubectl apply -f - -kubectl create secret -n argocd generic aws-creds --from-literal=AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} --from-literal=AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} --dry-run -oyaml | kubectl apply -f - -# kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f - # TODO: kubernetes 1.19 and above -kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml -echo "argocd created" - -echo "sleeping 60 seconds after argocd creation" -sleep 10 -echo "sleeping 50 more seconds" -sleep 10 -echo "sleeping 40 more seconds" -sleep 10 -echo "sleeping 30 more seconds" -sleep 10 -echo "sleeping 20 more seconds" -sleep 10 -echo "sleeping 10 more seconds" -sleep 10 - -echo "connecting to argocd in background process" -kubectl -n argocd port-forward svc/argocd-server -n argocd 8080:443 & -echo "connection to argocd established" - -echo "collecting argocd connection details" -export ARGOCD_AUTH_PASSWORD=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d) -export TF_VAR_argocd_auth_password=${ARGOCD_AUTH_PASSWORD} -export ARGOCD_AUTH_USERNAME=admin -export ARGOCD_INSECURE=true -export ARGOCD_SERVER=localhost:8080 - -cd /git/gitops/terraform/argocd -echo "applying argocd terraform" -terraform init -terraform apply -target module.argocd_repos -auto-approve -terraform apply -target module.argocd_registry -auto-approve -# terraform destroy -target module.argocd_registry -target module.argocd_repos -auto-approve; exit 1 # TODO: hack -echo "argocd terraform complete" - - - -echo "ARGOCD_AUTH_PASSWORD: $ARGOCD_AUTH_PASSWORD" -argocd login localhost:8080 --insecure --username admin --password "${ARGOCD_AUTH_PASSWORD}" -echo "sleeping 120 seconds before checking vault status" -sleep 30 -echo "sleeping 90 more seconds before checking vault status" -sleep 30 -echo "sleeping 60 more seconds before checking vault status" -sleep 30 -echo "sleeping 30 more seconds before checking vault status" -sleep 30 -argocd app wait vault - -export VAULT_TOKEN=$(kubectl -n vault get secret vault-unseal-keys -ojson | jq -r '.data."cluster-keys.json"' | base64 -d | jq -r .root_token) -export VAULT_ADDR="https://vault.${AWS_HOSTED_ZONE_NAME}" -export TF_VAR_vault_addr=$VAULT_ADDR -export TF_VAR_vault_token=$VAULT_TOKEN -export TF_VAR_gitlab_runner_token=$(cat /git/gitops/terraform/.gitlab-runner-registration-token) - -/scripts/nebulous/wait-for-200.sh "https://vault.${AWS_HOSTED_ZONE_NAME}/ui/vault/auth?with=token" - -# apply terraform -if [ -z "$SKIP_VAULT_APPLY" ] -then - echo - echo '########################################' - echo '#' - echo '# VAULT TERRAFORM' - echo '#' - echo '########################################' - echo - - cd /git/gitops/terraform/vault - echo "applying vault terraform" - terraform init - terraform apply -target module.bootstrap -auto-approve - # terraform destroy -target module.bootstrap -auto-approve; exit 1 # TODO: hack - echo "vault terraform complete" - - echo "waiting 300 seconds after vault terraform apply" - sleep 30 - echo "waiting 270 more seconds after vault terraform apply" - sleep 30 - echo "waiting 240 more seconds after vault terraform apply" - sleep 30 - echo "waiting 210 more seconds after vault terraform apply" - sleep 30 - echo "waiting 180 more seconds after vault terraform apply" - sleep 30 - echo "waiting 150 more seconds after vault terraform apply" - sleep 30 - echo "waiting 120 more seconds after vault terraform apply" - sleep 30 - echo "waiting 90 more seconds after vault terraform apply" - sleep 30 - echo "waiting 60 more seconds after vault terraform apply" - sleep 30 - echo "waiting 30 more seconds after vault terraform apply" - sleep 30 - -else - echo "skipping vault terraform because SKIP_VAULT_APPLY is set" -fi - -if [ -z "$SKIP_SSH_STORAGE" ] -then - echo "writing ssh key to secret/ssh" - vault login -no-print $VAULT_TOKEN - vault kv put secret/ssh/terraform_ssh_key terraform_ssh_key_base64="$(cat /git/gitops/terraform/base/terraform-ssh-key | base64)" - vault kv put secret/ssh/terraform_ssh_key_pub terraform_ssh_key_pub_base64="$(cat /git/gitops/terraform/base/terraform-ssh-key.pub | base64)" -fi - -# the following comnmand is a bit fickle as the vault dns propagates, -# a retry attempts to make this a bit more fault tolerant to that -echo "argocd app sync of external-secrets-store" -for i in 1 2 3 4 5 6 7 8; do argocd app sync external-secrets-store && break || echo "sync of external-secrets-store did not complete successfully. this is often due to delays in dns propagation. sleeping for 60s before retry" && sleep 60; done -echo "argocd app sync of gitlab-runner" -for i in 1 2 3 4 5 6 7 8; do argocd app sync gitlab-runner-components && break || echo "sync of gitlab-runner did not complete successfully. this is often due to delays in dns propagation. sleeping for 60s before retry" && sleep 60; done -echo "argocd app sync of chartmuseum" -for i in 1 2 3 4 5 6 7 8; do argocd app sync chartmuseum-components && break || echo "sync of chartmuseum did not complete successfully. this is often due to delays in dns propagation. sleeping for 60s before retry" && sleep 60; done -echo "argocd app sync of atlantis" -for i in 1 2 3 4 5 6 7 8; do argocd app sync atlantis-components && break || echo "sync of atlantis did not complete successfully. this is often due to delays in dns propagation. sleeping for 60s before retry" && sleep 60; done - -echo "awaiting successful sync of external-secrets-store" -argocd app wait external-secrets-store - -echo "awaiting successful sync of gitlab-runner" -argocd app wait gitlab-runner-components -argocd app wait gitlab-runner - -echo "awaiting successful sync of chartmuseum" -argocd app wait chartmuseum-components -argocd app wait chartmuseum - -echo "awaiting successful sync of atlantis" -argocd app wait atlantis-components -argocd app wait atlantis - -if [ -z "$SKIP_USERS_APPLY" ] -then - echo - echo '########################################' - echo '#' - echo '# USERS' - echo '#' - echo '########################################' - echo - - cd /git/gitops/terraform/users - echo "applying users terraform" - terraform init - terraform apply -auto-approve - # terraform destroy -auto-approve; exit 1 # TODO: hack - echo "users terraform complete" - sleep 10 -fi - -if [ -z "$SKIP_OIDC_PATCHING" ] -then - echo - echo '########################################' - echo '#' - echo '# OIDC PATCHING' - echo '#' - echo '########################################' - echo - - echo "creating gitlab oidc backend for vault" - APP_NAME=vault - export VAULT_APP_DETAILS=$(curl --request POST --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --data "name=${APP_NAME}&redirect_uri=https://${APP_NAME}.${AWS_HOSTED_ZONE_NAME}:8250/oidc/callback http://localhost:8250/oidc/callback https://${APP_NAME}.${AWS_HOSTED_ZONE_NAME}/ui/vault/auth/oidc/oidc/callback http://localhost:8200/ui/vault/auth/oidc/oidc/callback&scopes=read_user email openid" "https://gitlab.${AWS_HOSTED_ZONE_NAME}/api/v4/applications") - export VAULT_APP_ID=$(echo $VAULT_APP_DETAILS | jq -r ".application_id") - export VAULT_APP_SECRET=$(echo $VAULT_APP_DETAILS | jq -r ".secret") - echo "backend created, storing secret in vault at secret/admin/oidc/${APP_NAME}" - curl \ - -H "X-Vault-Token: ${VAULT_TOKEN}" \ - -H "Content-Type: application/json" \ - -X POST \ - -d "{\"data\": ${VAULT_APP_DETAILS}}" \ - https://vault.${AWS_HOSTED_ZONE_NAME}/v1/secret/data/admin/oidc/${APP_NAME} - - echo "creating gitlab oidc backend for argo" - APP_NAME=argo - export ARGO_APP_DETAILS=$(curl --request POST --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --data "name=${APP_NAME}&redirect_uri=https://${APP_NAME}.${AWS_HOSTED_ZONE_NAME}/oauth2/callback&scopes=read_user email openid" "https://gitlab.${AWS_HOSTED_ZONE_NAME}/api/v4/applications") - export ARGO_APP_ID=$(echo $ARGO_APP_DETAILS | jq -r ".application_id") - export ARGO_APP_SECRET=$(echo $ARGO_APP_DETAILS | jq -r ".secret") - echo "backend created, storing secret in vault at secret/admin/oidc/${APP_NAME}" - curl \ - -H "X-Vault-Token: ${VAULT_TOKEN}" \ - -H "Content-Type: application/json" \ - -X POST \ - -d "{\"data\": ${ARGO_APP_DETAILS}}" \ - https://vault.${AWS_HOSTED_ZONE_NAME}/v1/secret/data/admin/oidc/${APP_NAME} - - echo "creating gitlab oidc backend for argocd" - APP_NAME=argocd - export ARGOCD_APP_DETAILS=$(curl --request POST --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --data "name=${APP_NAME}&redirect_uri=https://${APP_NAME}.${AWS_HOSTED_ZONE_NAME}/auth/callback&scopes=email openid" "https://gitlab.${AWS_HOSTED_ZONE_NAME}/api/v4/applications") - export ARGOCD_APP_ID=$(echo $ARGOCD_APP_DETAILS | jq -r ".application_id") - export ARGOCD_APP_SECRET=$(echo $ARGOCD_APP_DETAILS | jq -r ".secret") - echo "backend created, storing secret in vault at secret/admin/oidc/${APP_NAME}" - curl \ - -H "X-Vault-Token: ${VAULT_TOKEN}" \ - -H "Content-Type: application/json" \ - -X POST \ - -d "{\"data\": ${ARGOCD_APP_DETAILS}}" \ - https://vault.${AWS_HOSTED_ZONE_NAME}/v1/secret/data/admin/oidc/${APP_NAME} - - echo "creating gitlab oidc backend for gitlab itself" - APP_NAME=gitlab - export GITLAB_APP_DETAILS=$(curl --request POST --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" --data "name=${APP_NAME}&redirect_uri=https://${APP_NAME}.${AWS_HOSTED_ZONE_NAME}&scopes=read_user email openid" "https://gitlab.${AWS_HOSTED_ZONE_NAME}/api/v4/applications") - export GITLAB_APP_ID=$(echo $GITLAB_APP_DETAILS | jq -r ".application_id") - export GITLAB_APP_SECRET=$(echo $GITLAB_APP_DETAILS | jq -r ".secret") - echo "backend created, storing secret in vault at secret/admin/oidc/${APP_NAME}" - curl \ - -H "X-Vault-Token: ${VAULT_TOKEN}" \ - -H "Content-Type: application/json" \ - -X POST \ - -d "{\"data\": ${GITLAB_APP_DETAILS}}" \ - https://vault.${AWS_HOSTED_ZONE_NAME}/v1/secret/data/admin/oidc/${APP_NAME} - - echo "adding oidc configs to argocd configmap" - kubectl -n argocd patch secret argocd-secret -p "{\"stringData\": {\"oidc.gitlab.clientSecret\": \"${ARGOCD_APP_SECRET}\"}}" - - - echo "configuring git client" - cd "/git/gitops" - git config --global user.name "Administrator" - git config --global user.email "${EMAIL_ADDRESS}" - git checkout main - git pull origin main --rebase - - echo "adding oidc config to argocd gitops registry" - # TODO: issuer may need a route added, may also need diff scopes - cat << EOF >> /git/gitops/components/argocd/configmap.yaml - - url: https://argocd.${AWS_HOSTED_ZONE_NAME} - oidc.config: | - name: Gitlab - issuer: https://gitlab.${AWS_HOSTED_ZONE_NAME} - clientID: ${ARGOCD_APP_ID} - clientSecret: \$oidc.gitlab.clientSecret - requestedScopes: ["openid", "email"] - -EOF - - git add . - git commit -m "updated oidc config for argocd" - git push -u origin main - echo "pushed to gitops origin" - # argocd app get argocd --hard-refresh - - - cd /git/gitops/terraform/vault - echo "applying vault terraform" - terraform init - terraform apply -auto-approve - # terraform destroy -auto-approve; exit 1 # TODO: hack - echo "vault terraform complete" -fi - - - -echo "kicking over argo-server pod" -kubectl -n argo delete pod $(kubectl -n argo get pods --selector=app=argo-server --no-headers -o custom-columns=":metadata.name") - -echo "argocd app sync of argo-components" -for i in 1 2 3 4 5 6 7 8; do argocd app sync argo-components && break || echo "sync of argo did not complete successfully. this is often due to delays in dns propagation. sleeping for 60s before retry" && sleep 60; done -echo "awaiting successful sync of argo-components" - -argocd app wait argo-components -echo "sync and wait of argo-components and argo is complete" - -echo "configuring git client" -cd /git/metaphor -git config --global user.name "Administrator" -git config --global user.email "${EMAIL_ADDRESS}" - -echo "triggering metaphor delivery pipeline" -cd /git/metaphor -git pull origin main --rebase -git commit --allow-empty -m "kubefirst trigger pipeline" -git push -u origin main -echo "metaphor delivery pipeline invoked" - -echo "triggering gitops pull request to test atlantis workflows" -cd /git/gitops -git pull origin main --rebase -SUFFIX=$RANDOM -git checkout -b "atlantis-test-${SUFFIX}" -echo "" >> /git/gitops/terraform/argocd/main.tf -echo "" >> /git/gitops/terraform/base/main.tf -echo "" >> /git/gitops/terraform/gitlab/kubefirst-repos.tf -echo "" >> /git/gitops/terraform/vault/main.tf -git add . -git commit -m "test the atlantis workflow" -git push -u origin atlantis-test-${SUFFIX} -o merge_request.create -echo "gitops pull request created" - - - - - -echo -echo -echo -echo -echo -echo -echo -echo "#############################################################################################" -echo "#" -echo "# !!!! !!! !!! !!! KEEP EVERYTHING PRINTED FROM THIS LINE DOWN !!!! !!! !!! !!!" -echo "#" -echo "#############################################################################################" -sleep 5 -echo -echo -echo "|--------------------------------------------------------" -echo "| GitLab" -echo "| https://gitlab.${AWS_HOSTED_ZONE_NAME}" -echo "| username: root" -echo "| password: ${GITLAB_BOT_ROOT_PASSWORD}" -echo "| Repos:" -echo "| https://gitlab.${AWS_HOSTED_ZONE_NAME}/kubefirst/gitops" -echo "| https://gitlab.${AWS_HOSTED_ZONE_NAME}/kubefirst/metaphor" -echo "| * sso enabled" -echo "|--------------------------------------------------------" -sleep 1 -echo "" -echo "" -echo "|--------------------------------------------------------" -echo "| Vault" -echo "| https://vault.${AWS_HOSTED_ZONE_NAME}" -echo "| method: token" -echo "| token: ${VAULT_TOKEN}" -echo "| * sso enabled" -echo "|--------------------------------------------------------" -sleep 1 -echo "" -echo "" -echo "|--------------------------------------------------------" -echo "| Argo CD" -echo "| https://argocd.${AWS_HOSTED_ZONE_NAME}" -echo "| username: admin" -echo "| password: ${ARGOCD_AUTH_PASSWORD}" -echo "| * sso enabled" -echo "|--------------------------------------------------------" -sleep 1 -echo "" -echo "" -echo "|--------------------------------------------------------" -echo "| Argo Workflows" -echo "| https://argo.${AWS_HOSTED_ZONE_NAME}" -echo "| sso credentials only" -echo "| * sso enabled" -echo "|--------------------------------------------------------" -sleep 1 -echo "" -echo "" -echo "|--------------------------------------------------------" -echo "| Atlantis" -echo "| https://atlantis.${AWS_HOSTED_ZONE_NAME}" -echo "|--------------------------------------------------------" -sleep 1 -echo "" -echo "" -echo "|--------------------------------------------------------" -echo "| Chart Museum" -echo "| https://chartmuseum.${AWS_HOSTED_ZONE_NAME}" -echo "| see vault for credentials" -echo "|--------------------------------------------------------" -sleep 1 -echo "" -echo "" -echo "|--------------------------------------------------------" -echo "| Metaphor" -echo "| Development: https://metaphor-development.${AWS_HOSTED_ZONE_NAME}" -echo "| Staging: https://metaphor-staging.${AWS_HOSTED_ZONE_NAME}" -echo "| Production: https://metaphor-production.${AWS_HOSTED_ZONE_NAME}" -echo "|--------------------------------------------------------" -sleep 1 -echo "" -echo "" -echo "|--------------------------------------------------------" -echo "| Kubernetes backend utilities" -echo "|--------------------------------------------------------" -echo "| Nginx Ingress Controller | ingress-nginx namespace |" -echo "| Cert Manager | cert-manager namespace |" -echo "| Certificate Issuers | clusterwide |" -echo "| External Secrets | external-secrets namespace |" -echo "| GitLab Runner | gitlab-runner namespace |" -echo "|--------------------------------------------------------" -sleep 2 -echo "" -echo "" -echo "" -echo "" -echo "" -echo "WARNING: Test your connection to Kubernetes, GitLab, and Vault BEFORE CLOSING THIS WINDOW. Connection details follow." -echo "Docs to install the tools mentioned: https://docs.kubefirst.com/tooling/tooling-overview.html" -echo "" -echo "" -echo "" -echo "" -echo "" -echo "#####################################################" -echo "# GITLAB" -echo "" -echo "To connect to your GitLab visit: https://gitlab.${AWS_HOSTED_ZONE_NAME}" -echo "username: root" -echo "password: ${GITLAB_BOT_ROOT_PASSWORD}" -echo "" -echo "Once logged into GitLab, visit:" -echo "- the gitops repo: https://gitlab.${AWS_HOSTED_ZONE_NAME}/kubefirst/gitops" -echo "- the metaphor repo: https://gitlab.${AWS_HOSTED_ZONE_NAME}/kubefirst/metaphor" -echo "" -echo "Please note: your self-hosted GitLab server has Open Registration, which allows" -echo "anyone to sign themselves up. To change this configuration see Sign-up Restrictions" -echo "https://gitlab.${AWS_HOSTED_ZONE_NAME}/admin/application_settings/general" -echo "#####################################################" -echo "" -echo "" -echo "#####################################################" -echo "KUBERNETES" -echo "" -echo "To connect to your kubernetes cluster, install the aws cli and run the following in your terminal:" -echo "export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" -echo "export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" -echo "export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}" -echo "aws eks update-kubeconfig --name kubefirst" -echo "" -echo "To test your new connection, install kubectl and run a few kubectl commands in your terminal:" -echo "kubectl get nodes -owide" -echo "kubectl get namespaces" -echo "#####################################################" -echo "" -echo "" -echo "#####################################################" -echo "VAULT" -echo "" -echo "To login to Vault visit https://vault.${AWS_HOSTED_ZONE_NAME}" -echo "Method: Token" -echo "Token: ${VAULT_TOKEN}" -echo "" -echo "Once you're logged into Vault confirm you can navigate to your infrastructure secrets in Vault at path:" -echo "secret/atlantis" -echo "#####################################################" -echo "" -echo "" -echo "#####################################################" -echo "MISC" -echo "" -echo "Your BUCKET_RAND value is: ${BUCKET_RAND}. You may need this value if you decide to run teardown." -echo "#####################################################" -echo "" -echo "" -echo "" -echo "" -echo "Once you've stored this output and tested the above connections," -echo "you should continue exploring the kubefirst platform from our docs:" -echo "" -echo "https://docs.kubefirst.com/kubefirst/getting-started.html <--- seriously." -echo "" -echo "" -echo "" -echo "" -echo "We poured our hearts into this project for 2 years to get you to this message." -echo "We would LOOOOOOVE a github star if you think we earned it." -echo "Top-right corner of this page: https://github.com/kubefirst/nebulous" -echo "Thanks so much crew." -echo "" -echo "- The Kubefirst Team" -echo "" -echo "" diff --git a/scripts/nebulous/source-profile.sh b/scripts/nebulous/source-profile.sh deleted file mode 100755 index 5aa3ff481..000000000 --- a/scripts/nebulous/source-profile.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -### -# usage: ./scripts/nebulous/source-profile.sh -### - -set -e - -export NVM_DIR="$HOME/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm - -# aws profile setup -mkdir -p ~/.aws -cat << EOF > ~/.aws/config -[default] -output = json -region = ${AWS_DEFAULT_REGION} -EOF - -cat << EOF > ~/.aws/credentials -[default] -aws_access_key_id = ${AWS_ACCESS_KEY_ID} -aws_secret_access_key = ${AWS_SECRET_ACCESS_KEY} -EOF diff --git a/scripts/nebulous/wait-for-200.sh b/scripts/nebulous/wait-for-200.sh deleted file mode 100755 index 80463d38a..000000000 --- a/scripts/nebulous/wait-for-200.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -### -# usage: ./scripts/nebulous/wait-for-200.sh "https://gitlab-kubefirst.example.com" -### - -set -e - -URL=$1 - -while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' $URL)" != "200" ]]; -do - echo "${URL} is not yet ready" - sleep 10; -done \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 000000000..037e3518a --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1 @@ +resource "null_resource" "foo" {} \ No newline at end of file From bd989f4a15fccdee80631694d6cb6949d9d5a0c0 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 29 Jun 2022 15:29:29 +0000 Subject: [PATCH 002/107] sign commit Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ebb3054d1..e002d957d 100644 --- a/README.md +++ b/README.md @@ -198,3 +198,4 @@ rm ~/.flare added gitlab.yaml to registry pushing local to soft origin + From c8db3e42728b430a6f0230b510fb052ee211eca2 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 29 Jun 2022 16:02:29 +0000 Subject: [PATCH 003/107] rename project Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/info.go | 2 +- go.mod | 2 +- main.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/info.go b/cmd/info.go index a63cf0ca3..319b9e78d 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -7,7 +7,7 @@ package cmd import ( "log" "github.com/spf13/cobra" - "gitlab.kubefirst.io/kubefirst/flare/pkg/flare" + "github.com/kubefirst/nebulous/pkg/flare" ) // infoCmd represents the info command diff --git a/go.mod b/go.mod index f380180b4..7904cb848 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module gitlab.kubefirst.io/kubefirst/flare +module github.com/kubefirst/nebulous go 1.17 diff --git a/main.go b/main.go index 67a9ffe4b..188f6831a 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ Copyright © 2022 Kubefirst Inc. devops@kubefirst.com package main import ( - "gitlab.kubefirst.io/kubefirst/flare/cmd" + "github.com/kubefirst/nebulous/cmd" "log" "os" "time" From ee55614ddb8e95d0b495b5a0f2ca0567dac5ddb4 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 29 Jun 2022 16:14:09 +0000 Subject: [PATCH 004/107] adding init/create steps Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 631 ++++++++++++++++++++++++++++ cmd/init.go | 1088 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1719 insertions(+) create mode 100644 cmd/create.go create mode 100644 cmd/init.go diff --git a/cmd/create.go b/cmd/create.go new file mode 100644 index 000000000..77d5172fa --- /dev/null +++ b/cmd/create.go @@ -0,0 +1,631 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "html/template" + "log" + "net/http" + "net/url" + "os" + "os/exec" + "strings" + "syscall" + "time" + + b64 "encoding/base64" + + "github.com/ghodss/yaml" + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/google/uuid" + vault "github.com/hashicorp/vault/api" + "github.com/spf13/cobra" + "github.com/spf13/viper" + gitlab "github.com/xanzy/go-gitlab" + "github.com/kubefirst/nebulous/pkg/flare" + v1 "k8s.io/api/core/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +// createCmd represents the create command +var createCmd = &cobra.Command{ + Use: "create", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + + metricName := "kubefirst.mgmt_cluster_install.started" + metricDomain := viper.GetString("aws.domainname") + + flare.SendTelemetry(metricDomain, metricName) + + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) + + applyBase := viper.GetBool("create.terraformapplied.base") + createSoftServeFlag := viper.GetBool("create.softserve.create") + configureAndPushFlag := viper.GetBool("create.softserve.configure") + + if applyBase != true { + + terraformAction := "apply" + + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) + + err := os.Chdir(directory) + if err != nil { + fmt.Println("error changing dir") + } + + viperDestoryFlag := viper.GetBool("terraform.destroy") + cmdDestroyFlag, _ := cmd.Flags().GetBool("destroy") + + if viperDestoryFlag == true || cmdDestroyFlag == true { + terraformAction = "destroy" + } + + fmt.Println("terraform action: ", terraformAction, "destroyFlag: ", viperDestoryFlag) + tfInitCmd := exec.Command(terraformPath, "init") + tfInitCmd.Stdout = os.Stdout + tfInitCmd.Stderr = os.Stderr + err = tfInitCmd.Run() + if err != nil { + fmt.Println("failed to call tfInitCmd.Run(): ", err) + } + tfApplyCmd := exec.Command(terraformPath, fmt.Sprintf("%s", terraformAction), "-auto-approve") + tfApplyCmd.Stdout = os.Stdout + tfApplyCmd.Stderr = os.Stderr + err = tfApplyCmd.Run() + if err != nil { + fmt.Println("failed to call tfApplyCmd.Run(): ", err) + panic("tfApplyCmd.Run() failed") + } + keyIdBytes, err := exec.Command(terraformPath, "output", "vault_unseal_kms_key").Output() + if err != nil { + fmt.Println("failed to call tfOutputCmd.Run(): ", err) + } + keyId := strings.TrimSpace(string(keyIdBytes)) + + fmt.Println("keyid is:", keyId) + viper.Set("vault.kmskeyid", keyId) + viper.Set("create.terraformapplied.base", true) + viper.WriteConfig() + + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) + + } + if createSoftServeFlag != true { + createSoftServe(kubeconfigPath) + viper.Set("create.softserve.create", true) + viper.WriteConfig() + fmt.Println("waiting for soft-serve installation to complete...") + time.Sleep(60 * time.Second) + + } + + if configureAndPushFlag != true { + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + fmt.Println("failed to call kPortForward.Run(): ", err) + } + time.Sleep(10 * time.Second) + + configureSoftServe() + pushGitopsToSoftServe() + viper.Set("create.softserve.configure", true) + viper.WriteConfig() + } + + time.Sleep(10 * time.Second) + + helmInstallArgocd(home, kubeconfigPath) + awaitGitlab() + + fmt.Println("discovering gitlab toolbox pod") + + var outb, errb bytes.Buffer + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pod", "-lapp=toolbox", "-o", "jsonpath='{.items[0].metadata.name}'") + k.Stdout = &outb + k.Stderr = &errb + err := k.Run() + if err != nil { + fmt.Println("failed to call k.Run() to get gitlab pod: ", err) + } + gitlabPodName := outb.String() + gitlabPodName = strings.Replace(gitlabPodName, "'", "", -1) + fmt.Println("gitlab pod", gitlabPodName) + + gitlabToken := viper.GetString("gitlab.token") + if gitlabToken == "" { + + fmt.Println("getting gitlab personal access token") + + id := uuid.New() + gitlabToken = id.String()[:20] + + k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + fmt.Println("failed to call k.Run() to set gitlab token: ", err) + } + + viper.Set("gitlab.token", gitlabToken) + viper.WriteConfig() + + fmt.Println("gitlabToken", gitlabToken) + } + + gitlabRunnerToken := viper.GetString("gitlab.runnertoken") + if gitlabRunnerToken == "" { + + fmt.Println("getting gitlab runner token") + + var tokenOut, tokenErr bytes.Buffer + k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "secret", "gitlab-gitlab-runner-secret", "-o", "jsonpath='{.data.runner-registration-token}'") + k.Stdout = &tokenOut + k.Stderr = &tokenErr + err = k.Run() + if err != nil { + fmt.Println("failed to call k.Run() to get gitlabRunnerRegistrationToken: ", err) + } + encodedToken := tokenOut.String() + fmt.Println(encodedToken) + encodedToken = strings.Replace(encodedToken, "'", "", -1) + fmt.Println(encodedToken) + gitlabRunnerRegistrationTokenBytes, err := base64.StdEncoding.DecodeString(encodedToken) + gitlabRunnerRegistrationToken := string(gitlabRunnerRegistrationTokenBytes) + fmt.Println(gitlabRunnerRegistrationToken) + if err != nil { + panic(err) + } + viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) + viper.WriteConfig() + fmt.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) + } + + if !viper.GetBool("create.terraformapplied.gitlab") { + // Prepare for terraform gitlab execution + os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) + os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) + + directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) + err = os.Chdir(directory) + if err != nil { + fmt.Println("error changing dir") + } + + tfInitCmd := exec.Command(terraformPath, "init") + tfInitCmd.Stdout = os.Stdout + tfInitCmd.Stderr = os.Stderr + err = tfInitCmd.Run() + if err != nil { + fmt.Println("failed to call tfInitCmd.Run(): ", err) + } + + tfApplyCmd := exec.Command(terraformPath, "apply", "-auto-approve") + tfApplyCmd.Stdout = os.Stdout + tfApplyCmd.Stderr = os.Stderr + err = tfApplyCmd.Run() + if err != nil { + fmt.Println("failed to call tfApplyCmd.Run(): ", err) + } + + viper.Set("create.terraformapplied.gitlab", true) + viper.WriteConfig() + } + + // upload ssh public key + if !viper.GetBool("gitlab.keyuploaded") { + fmt.Println("uploading ssh public key to gitlab") + data := url.Values{ + "title": {"kubefirst"}, + "key": {viper.GetString("botpublickey")}, + } + + gitlabUrlBase := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) + + resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) + if err != nil { + log.Fatal(err) + } + var res map[string]interface{} + json.NewDecoder(resp.Body).Decode(&res) + fmt.Println(res) + fmt.Println("ssh public key uploaded to gitlab") + viper.Set("gitlab.keyuploaded", true) + viper.WriteConfig() + } else { + fmt.Println("ssh public key already uploaded to gitlab") + } + + pushGitopsToGitLab() + changeRegistryToGitLab() + configureVault() + addGitlabOidcApplications() + hydrateGitlabMetaphorRepo() + metricName = "kubefirst.mgmt_cluster_install.completed" + + flare.SendTelemetry(metricDomain, metricName) + }, +} + +func hydrateGitlabMetaphorRepo() { + + metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", home) + + url := "https://github.com/kubefirst/metaphor-template" + + metaphorTemplateRepo, err := git.PlainClone(metaphorTemplateDir, false, &git.CloneOptions{ + URL: url, + }) + if err != nil { + panic("error cloning metaphor-template repo") + } + + detokenize(metaphorTemplateDir) + + // todo make global + domainName := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) + fmt.Println("git remote add origin", domainName) + _, err = metaphorTemplateRepo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "gitlab", + URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.git", domainName)}, + }) + + w, _ := metaphorTemplateRepo.Worktree() + + fmt.Println("Committing new changes...") + w.Add(".") + w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: installerEmail, + When: time.Now(), + }, + }) + + err = metaphorTemplateRepo.Push(&git.PushOptions{ + RemoteName: "gitlab", + Auth: &gitHttp.BasicAuth{ + Username: "root", + Password: viper.GetString("gitlab.token"), + }, + }) + if err != nil { + fmt.Println("error pushing to remote", err) + } + +} + +func changeRegistryToGitLab() { + if !viper.GetBool("gitlab.registry") { + + type ArgocdGitCreds struct { + PersonalAccessToken string + URL string + FullURL string + } + + pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) + url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.domainname")))) + fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.domainname")))) + + creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} + + var argocdRepositoryAccessTokenSecret *v1.Secret + kubeconfig := home + "/.kubefirst/gitops/terraform/base/kubeconfig_kubefirst" + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + panic(err.Error()) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) + } + argocdSecretClient = clientset.CoreV1().Secrets("argocd") + + var secrets bytes.Buffer + + c, err := template.New("creds-gitlab").Parse(` + apiVersion: v1 + data: + password: {{ .PersonalAccessToken }} + url: {{ .URL }} + username: cm9vdA== + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repo-creds + name: creds-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&secrets, creds); err != nil { + log.Panic(err) + } + fmt.Println(secrets.String()) + + ba := []byte(secrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + + _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + panic(err) + } + + var repoSecrets bytes.Buffer + + c, err = template.New("repo-gitlab").Parse(` + apiVersion: v1 + data: + project: ZGVmYXVsdA== + type: Z2l0 + url: {{ .FullURL }} + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repository + name: repo-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&repoSecrets, creds); err != nil { + log.Panic(err) + } + fmt.Println(repoSecrets.String()) + + ba = []byte(repoSecrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + + _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + panic(err) + } + + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", home)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + fmt.Println("failed to call k.Run() to apply argocd patch to adopt gitlab: ", err) + } + + viper.Set("gitlab.registry", true) + viper.WriteConfig() + } +} + +func addGitlabOidcApplications() { + domain := viper.GetString("aws.domainname") + git, err := gitlab.NewClient( + viper.GetString("gitlab.token"), + gitlab.WithBaseURL(fmt.Sprintf("https://gitlab.%s/api/v4", domain)), + ) + if err != nil { + log.Fatal(err) + } + + apps := []string{"argo", "argocd", "vault"} + cb := make(map[string]string) + cb["argo"] = fmt.Sprintf("https://argo.%s/oauth2/callback", domain) + cb["argocd"] = fmt.Sprintf("https://argocd.%s/auth/callback", domain) + cb["vault"] = fmt.Sprintf("https://vault.%s:8250/oidc/callback http://localhost:8250/oidc/callback https://vault.%s/ui/vault/auth/oidc/oidc/callback http://localhost:8200/ui/vault/auth/oidc/oidc/callback", domain, domain) + + for _, app := range apps { + fmt.Println("checking to see if", app, "oidc application needs to be created in gitlab") + appId := viper.GetString(fmt.Sprintf("gitlab.oidc.%s.applicationid", app)) + if appId == "" { + + // Create an application + opts := &gitlab.CreateApplicationOptions{ + Name: gitlab.String(app), + RedirectURI: gitlab.String(cb[app]), + Scopes: gitlab.String("read_user openid email"), + } + createdApp, _, err := git.Applications.CreateApplication(opts) + if err != nil { + log.Fatal(err) + } + + // List all applications + existingApps, _, err := git.Applications.ListApplications(&gitlab.ListApplicationsOptions{}) + if err != nil { + log.Fatal(err) + } + + created := false + for _, existingApp := range existingApps { + if existingApp.ApplicationName == app { + created = true + } + } + if created { + fmt.Println("created gitlab oidc application with applicationid", createdApp.ApplicationID) + viper.Set(fmt.Sprintf("gitlab.oidc.%s.applicationid", app), createdApp.ApplicationID) + viper.Set(fmt.Sprintf("gitlab.oidc.%s.secret", app), createdApp.Secret) + + secretData := map[string]interface{}{ + "data": map[string]interface{}{ + "application_id": createdApp.ApplicationID, + "secret": createdApp.Secret, + }, + } + secretPath := fmt.Sprintf("secret/data/oidc/%s", app) + addVaultSecret(secretPath, secretData) + viper.WriteConfig() + } else { + log.Panic("could not create gitlab iodc application", app) + } + } + } +} + +func addVaultSecret(secretPath string, secretData map[string]interface{}) { + fmt.Println("vault called") + + config := vault.DefaultConfig() + + config.Address = fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname")) + + client, err := vault.NewClient(config) + if err != nil { + fmt.Println("unable to initialize Vault client: ", err) + } + + client.SetToken(viper.GetString("vault.token")) + + // Writing a secret + _, err = client.Logical().Write(secretPath, secretData) + if err != nil { + fmt.Println("unable to write secret: ", err) + } else { + fmt.Println("secret written successfully.") + } +} + +func configureVault() { + if !viper.GetBool("create.terraformapplied.vault") { + + // ``` + // NOTE: the terraform here produces unnecessary $var.varname vars in the atlantis secret for nonsensitive values + // the following atlantis secrets shouldn't have vars in the gitops source code for the atlantis secret, they + // should look like us-east-1, in flat string code as non-sensitive vals - refactor soon. + // "TF_VAR_aws_region": "us-east-1", + // "TF_VAR_aws_account_id": "${var.aws_account_id}", + // "TF_VAR_email_address": "${var.email_address}", + // "TF_VAR_hosted_zone_id": "${var.hosted_zone_id}", + // "TF_VAR_hosted_zone_name": "${var.hosted_zone_name}", + // "TF_VAR_vault_addr": "${var.vault_addr}", + // ``` + // ... obviously keep the sensitive values bound to vars + + var outb, errb bytes.Buffer + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "get", "secret", "vault-unseal-keys", "-o", "jsonpath='{.data.cluster-keys\\.json}'") + k.Stdout = &outb + k.Stderr = &errb + err := k.Run() + if err != nil { + fmt.Println("failed to call k.Run() to get gitlab pod: ", err) + } + vaultKeysEncoded := outb.String() + vaultKeysEncoded = strings.Replace(vaultKeysEncoded, "'", "", -1) + fmt.Println("vault keys", vaultKeysEncoded) + + vaultKeysBytes, err := base64.StdEncoding.DecodeString(vaultKeysEncoded) + fmt.Println(vaultKeysBytes) + if err != nil { + panic(err) + } + vaultKeys := string(vaultKeysBytes) + fmt.Println(vaultKeys) + + var dat map[string]interface{} + if err := json.Unmarshal([]byte(vaultKeys), &dat); err != nil { + panic(err) + } + vaultToken := dat["root_token"].(string) + fmt.Println(vaultToken) + viper.Set("vault.token", vaultToken) + viper.WriteConfig() + + // Prepare for terraform vault execution + os.Setenv("VAULT_ADDR", fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname"))) + os.Setenv("VAULT_TOKEN", vaultToken) + + os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname"))) + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_email_address", viper.GetString("adminemail")) + os.Setenv("TF_VAR_gitlab_runner_token", viper.GetString("gitlab.runnertoken")) + os.Setenv("TF_VAR_gitlab_token", viper.GetString("gitlab.token")) + os.Setenv("TF_VAR_hosted_zone_id", viper.GetString("aws.domainid")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) + os.Setenv("TF_VAR_vault_token", viper.GetString("aws.domainname")) + os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") + + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", home) + err = os.Chdir(directory) + if err != nil { + fmt.Println("error changing dir") + } + + tfInitCmd := exec.Command(terraformPath, "init") + tfInitCmd.Stdout = os.Stdout + tfInitCmd.Stderr = os.Stderr + err = tfInitCmd.Run() + if err != nil { + fmt.Println("failed to call vault terraform init: ", err) + } + + tfApplyCmd := exec.Command(terraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") + tfApplyCmd.Stdout = os.Stdout + tfApplyCmd.Stderr = os.Stderr + err = tfApplyCmd.Run() + if err != nil { + fmt.Println("failed to call vault terraform apply: ", err) + } + + viper.Set("create.terraformapplied.vault", true) + viper.WriteConfig() + } +} + +func awaitGitlab() { + + fmt.Println("awaitGitlab called") + max := 200 + for i := 0; i < max; i++ { + + // todo should this be aws.hostedzonedname since we're sticking to an + // todo aws: and gcp: figure their nomenclature is more familar + hostedZoneName := viper.GetString("aws.domainname") + + resp, _ := http.Get(fmt.Sprintf("https://gitlab.%s", hostedZoneName)) + if resp != nil && resp.StatusCode == 200 { + fmt.Println("gitlab host resolved, 30 second grace period required...") + time.Sleep(time.Second * 30) + i = max + } else { + fmt.Println("gitlab host not resolved, sleeping 10s") + time.Sleep(time.Second * 10) + } + } +} + +func init() { + nebulousCmd.AddCommand(createCmd) + + // createCmd.Flags().String("tf-entrypoint", "", "the entrypoint to execute the terraform from") + // createCmd.MarkFlagRequired("tf-entrypoint") + // todo make this an optional switch and check for it or viper + createCmd.Flags().Bool("destroy", false, "destroy resources") + +} diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 000000000..b52486f90 --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,1088 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "archive/tar" + "archive/zip" + "compress/gzip" + "context" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/http" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/route53" + "github.com/aws/aws-sdk-go-v2/service/route53/types" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/cip8/autoname" + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/kubefirst/nebulous/pkg/flare" + gitlabSsh "github.com/kubefirst/nebulous/pkg/ssh" + ssh2 "golang.org/x/crypto/ssh" +) + +var Trackers map[string]*flare.ActionTracker + +const trackerStage0 = "1 - Load properties" +const trackerStage1 = "2 - Set .flare initial values" +const trackerStage2 = "3 - Test Domain Liveness" +const trackerStage3 = "4 - Create SSH Key Pair" +const trackerStage4 = "5 - Load Templates" +const trackerStage5 = "6 - Download Tools" +const trackerStage6 = "7 - Get Account Info" +const trackerStage7 = "8 - Create Buckets" +const trackerStage8 = "9 - Detokenize" +const trackerStage9 = "10 - Send Telemetry" + +// initCmd represents the init command +var initCmd = &cobra.Command{ + Use: "init", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + + flare.SetupProgress(10) + Trackers = make(map[string]*flare.ActionTracker) + Trackers[trackerStage0] = &flare.ActionTracker{flare.CreateTracker(trackerStage0, int64(1))} + Trackers[trackerStage1] = &flare.ActionTracker{flare.CreateTracker(trackerStage1, int64(1))} + Trackers[trackerStage2] = &flare.ActionTracker{flare.CreateTracker(trackerStage2, int64(1))} + Trackers[trackerStage3] = &flare.ActionTracker{flare.CreateTracker(trackerStage3, int64(1))} + Trackers[trackerStage4] = &flare.ActionTracker{flare.CreateTracker(trackerStage4, int64(1))} + Trackers[trackerStage5] = &flare.ActionTracker{flare.CreateTracker(trackerStage5, int64(3))} + Trackers[trackerStage6] = &flare.ActionTracker{flare.CreateTracker(trackerStage6, int64(1))} + Trackers[trackerStage7] = &flare.ActionTracker{flare.CreateTracker(trackerStage7, int64(4))} + Trackers[trackerStage8] = &flare.ActionTracker{flare.CreateTracker(trackerStage8, int64(1))} + Trackers[trackerStage9] = &flare.ActionTracker{flare.CreateTracker(trackerStage9, int64(1))} + infoCmd.Run(cmd, args) + metricName := "kubefirst.init.started" + metricDomain := "kubefirst.com" + if !dryrunMode { + flare.SendTelemetry(metricDomain, metricName) + } else { + log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) + } + + // todo hack + awsProfileSet := os.Getenv("AWS_PROFILE") + + if awsProfileSet == "" { + log.Println("\nhack: !!!!! PLEASE SET AWS PROFILE !!!!!\n\nexport AWS_PROFILE=starter\n") + os.Exit(1) + } + + // todo need to check flags and create config + + // hosted zone name: + // name of the hosted zone to be used for the kubefirst install + // if suffixed with a dot (eg. kubefirst.com.), the dot will be stripped + hostedZoneName, _ := cmd.Flags().GetString("hosted-zone-name") + if strings.HasSuffix(hostedZoneName, ".") { + hostedZoneName = hostedZoneName[:len(hostedZoneName)-1] + } + log.Println("hostedZoneName:", hostedZoneName) + viper.Set("aws.domainname", hostedZoneName) + viper.WriteConfig() + // admin email + // used for letsencrypt notifications and the gitlab root account + adminEmail, _ := cmd.Flags().GetString("admin-email") + log.Println("adminEmail:", adminEmail) + viper.Set("adminemail", adminEmail) + + // region + // name of the cloud region to provision resources when resources are region-specific + region, _ := cmd.Flags().GetString("region") + viper.Set("aws.region", region) + log.Println("region:", region) + + // hosted zone id + // so we don't have to keep looking it up from the domain name to use it + hostedZoneId := getDNSInfo(hostedZoneName) + // viper values set in above function + log.Println("hostedZoneId:", hostedZoneId) + Trackers[trackerStage0].Tracker.Increment(int64(1)) + Trackers[trackerStage1].Tracker.Increment(int64(1)) + //trackProgress(1, false) + // todo: this doesn't default to testing the dns check + if !viper.GetBool("init.hostedzonecheck.enabled") { + log.Println("skipping hosted zone check") + } else { + testHostedZoneLiveness(hostedZoneName, hostedZoneId) + } + Trackers[trackerStage2].Tracker.Increment(int64(1)) + // todo generate ssh key --> ~/.kubefirst/ssh-key .pub + + //! step 1 + // todo rm -rf ~/.kubefirst + // todo make sure - k -n soft-serve port-forward svc/soft-serve 8022:22 + + log.Println("calling createSshKeyPair() ") + createSshKeyPair() + log.Println("createSshKeyPair() complete\n\n") + Trackers[trackerStage3].Tracker.Increment(int64(1)) + + log.Println("calling cloneGitOpsRepo() function\n") + cloneGitOpsRepo() + log.Println("cloneGitOpsRepo() complete\n\n") + Trackers[trackerStage4].Tracker.Increment(int64(1)) + + log.Println("calling download() ") + download() + log.Println("download() complete\n\n") + + log.Println("calling getAccountInfo() function\n") + getAccountInfo() + log.Println("getAccountInfo() complete\n\n") + Trackers[trackerStage6].Tracker.Increment(int64(1)) + + log.Println("calling bucketRand() function\n") + bucketRand() + log.Println("bucketRand() complete\n\n") + + fmt.Println("calling detokenize() ") + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) + fmt.Println("detokenize() complete\n\n") + Trackers[trackerStage8].Tracker.Increment(int64(1)) + + // modConfigYaml() + metricName = "kubefirst.init.completed" + + if !dryrunMode { + flare.SendTelemetry(metricDomain, metricName) + } else { + log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) + } + + viper.WriteConfig() + Trackers[trackerStage9].Tracker.Increment(int64(1)) + time.Sleep(time.Millisecond * 100) + }, +} + +func init() { + nebulousCmd.AddCommand(initCmd) + + initCmd.Flags().String("hosted-zone-name", "", "the domain to provision the kubefirst platofrm in") + initCmd.MarkFlagRequired("hosted-zone-name") + initCmd.Flags().String("admin-email", "", "the email address for the administrator as well as for lets-encrypt certificate emails") + initCmd.MarkFlagRequired("admin-email") + initCmd.Flags().String("cloud", "", "the cloud to provision infrastructure in") + initCmd.MarkFlagRequired("cloud") + initCmd.Flags().String("region", "", "the region to provision the cloud resources in") + initCmd.MarkFlagRequired("region") + initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.flare") + + log.SetPrefix("LOG: ") + log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) + + initCmd.PersistentFlags().BoolVarP(&dryrunMode, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") + log.Println("init started") + +} + +func bucketRand() { + sess, err := session.NewSession(&aws.Config{ + Region: aws.String(viper.GetString("aws.region"))}, + ) + if err != nil { + log.Println("failed to attempt bucket creation ", err.Error()) + os.Exit(1) + } + + s3Client := s3.New(sess) + + randomName := strings.ReplaceAll(autoname.Generate(), "_", "-") + viper.Set("bucket.rand", randomName) + + buckets := strings.Fields("state-store argo-artifacts gitlab-backup chartmuseum") + for _, bucket := range buckets { + bucketExists := viper.GetBool(fmt.Sprintf("bucket.%s.created", bucket)) + if !bucketExists { + bucketName := fmt.Sprintf("k1-%s-%s", bucket, randomName) + + log.Println("creating", bucket, "bucket", bucketName) + + regionName := viper.GetString("aws.region") + log.Println("region is ", regionName) + if !dryrunMode { + if regionName == "us-east-1" { + _, err = s3Client.CreateBucket(&s3.CreateBucketInput{ + Bucket: &bucketName, + }) + } else { + _, err = s3Client.CreateBucket(&s3.CreateBucketInput{ + Bucket: &bucketName, + CreateBucketConfiguration: &s3.CreateBucketConfiguration{ + LocationConstraint: aws.String(regionName), + }, + }) + } + if err != nil { + log.Println("failed to create bucket "+bucketName, err.Error()) + os.Exit(1) + } + } else { + log.Printf("[#99] Dry-run mode, bucket creation skipped: %s", bucketName) + } + viper.Set(fmt.Sprintf("bucket.%s.created", bucket), true) + viper.Set(fmt.Sprintf("bucket.%s.name", bucket), bucketName) + viper.WriteConfig() + } + log.Printf("bucket %s exists", viper.GetString(fmt.Sprintf("bucket.%s.name", bucket))) + Trackers[trackerStage7].Tracker.Increment(int64(1)) + } +} + +func getAccountInfo() { + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + log.Println("failed to load configuration, error:", err) + } + // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration + stsClient := sts.NewFromConfig(cfg) + iamCaller, err := stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{}) + if err != nil { + log.Println("oh no error on call", err) + } + + viper.Set("aws.accountid", *iamCaller.Account) + viper.Set("aws.userarn", *iamCaller.Arn) + viper.WriteConfig() +} + +func testHostedZoneLiveness(hostedZoneName, hostedZoneId string) { + //tracker := progress.Tracker{Message: "testing hosted zone", Total: 25} + + // todo need to create single client and pass it + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + log.Println("failed to load configuration, error:", err) + } + // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration + route53Client := route53.NewFromConfig(cfg) + + // todo when checking to see if hosted zone exists print ns records for user to verity in dns registrar + route53RecordName := fmt.Sprintf("kubefirst-liveness.%s", hostedZoneName) + route53RecordValue := "domain record propagated" + + log.Println("checking to see if record", route53RecordName, "exists") + log.Println("hostedZoneId", hostedZoneId) + log.Println("route53RecordName", route53RecordName) + + recordList, err := route53Client.ListResourceRecordSets(context.TODO(), &route53.ListResourceRecordSetsInput{ + HostedZoneId: aws.String(hostedZoneId), + StartRecordName: aws.String(route53RecordName), + StartRecordType: "TXT", + }) + if err != nil { + log.Println("failed read route53 ", err.Error()) + os.Exit(1) + } + + if len(recordList.ResourceRecordSets) == 0 { + if !dryrunMode { + record, err := route53Client.ChangeResourceRecordSets(context.TODO(), &route53.ChangeResourceRecordSetsInput{ + ChangeBatch: &types.ChangeBatch{ + Changes: []types.Change{ + { + Action: "CREATE", + ResourceRecordSet: &types.ResourceRecordSet{ + Name: aws.String(route53RecordName), + Type: "TXT", + ResourceRecords: []types.ResourceRecord{ + { + Value: aws.String(strconv.Quote(route53RecordValue)), + }, + }, + TTL: aws.Int64(10), + Weight: aws.Int64(100), + SetIdentifier: aws.String("CREATE sanity check for kubefirst installation"), + }, + }, + }, + Comment: aws.String("CREATE sanity check dns record."), + }, + HostedZoneId: aws.String(hostedZoneId), + }) + if err != nil { + log.Println(err) + } + log.Println("record creation status is ", record.ChangeInfo.Status) + } else { + log.Printf("[#99] Dry-run mode, route53 creation/update skipped: %s", route53RecordName) + } + } + count := 0 + // todo need to exit after n number of minutes and tell them to check ns records + // todo this logic sucks + for count <= 25 { + count++ + //tracker.Increment(1) + //log.Println(text.Faint.Sprintf("[INFO] dns test %d of 25", count)) + + log.Println(route53RecordName) + ips, err := net.LookupTXT(route53RecordName) + + log.Println(ips) + + if err != nil { + // tracker.Message = fmt.Sprintln("dns test", count, "of", 25) + fmt.Fprintf(os.Stderr, "Could not get record name %s - waiting 10 seconds and trying again: %v\n", route53RecordName, err) + time.Sleep(10 * time.Second) + } else { + for _, ip := range ips { + // todo check ip against route53RecordValue in some capacity so we can pivot the value for testing + log.Printf("%s. in TXT record value: %s\n", route53RecordName, ip) + //tracker.MarkAsDone() + count = 26 + } + } + if count == 25 { + log.Println("unable to resolve hosted zone dns record. please check your domain registrar") + //tracker.MarkAsErrored() + //pw.Stop() + os.Exit(1) + } + } + // todo delete route53 record + + // recordDelete, err := route53Client.ChangeResourceRecordSets(context.TODO(), &route53.ChangeResourceRecordSetsInput{ + // ChangeBatch: &types.ChangeBatch{ + // Changes: []types.Change{ + // { + // Action: "DELETE", + // ResourceRecordSet: &types.ResourceRecordSet{ + // Name: aws.String(route53RecordName), + // Type: "A", + // ResourceRecords: []types.ResourceRecord{ + // { + // Value: aws.String(route53RecordValue), + // }, + // }, + // TTL: aws.Int64(10), + // Weight: aws.Int64(100), + // SetIdentifier: aws.String("CREATE sanity check for kubefirst installation"), + // }, + // }, + // }, + // Comment: aws.String("CREATE sanity check dns record."), + // }, + // HostedZoneId: aws.String(hostedZoneId), + // }) + // if err != nil { + // fmt.Println("error deleting route 53 record after liveness test") + // } + // fmt.Println("record deletion status is ", *&recordDelete.ChangeInfo.Status) + +} + +func modConfigYaml() { + + file, err := ioutil.ReadFile("./config.yaml") + if err != nil { + log.Println("error reading file", err) + } + + newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) + + err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) + if err != nil { + panic(err) + } +} + +func getDNSInfo(hostedZoneName string) string { + + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + log.Println("failed to load configuration, error:", err) + } + // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration + route53Client := route53.NewFromConfig(cfg) + hostedZones, err := route53Client.ListHostedZonesByName(context.TODO(), &route53.ListHostedZonesByNameInput{ + DNSName: &hostedZoneName, + }) + if err != nil { + log.Println("oh no error on call", err) + } + + var zoneId string + + for _, zone := range hostedZones.HostedZones { + if *zone.Name == fmt.Sprintf(`%s%s`, hostedZoneName, ".") { + zoneId = returnHostedZoneId(*zone.Id) + log.Printf(`found entry for user submitted domain %s, using hosted zone id %s`, hostedZoneName, zoneId) + viper.Set("aws.domainname", hostedZoneName) + viper.Set("aws.domainid", zoneId) + viper.WriteConfig() + } + } + return zoneId + +} + +func returnHostedZoneId(rawZoneId string) string { + return strings.Split(rawZoneId, "/")[2] +} + +func publicKey() (*ssh.PublicKeys, error) { + var publicKey *ssh.PublicKeys + publicKey, err := ssh.NewPublicKeys("git", []byte(viper.GetString("botprivatekey")), "") + if err != nil { + return nil, err + } + return publicKey, err +} + +func cloneGitOpsRepo() { + + url := "https://github.com/kubefirst/gitops-template" + directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + + // Clone the given repository to the given directory + log.Println("git clone", url, directory) + + _, err := git.PlainClone(directory, false, &git.CloneOptions{ + URL: url, + }) + if err != nil { + log.Println(err) + } + + println("downloaded gitops repo from template to directory", home, "/.kubefirst/gitops") +} + +func configureSoftServe() { + // todo clone repo + // todo manipulate config.yaml + // todo git add / commit / push + url := "ssh://127.0.0.1:8022/config" + directory := fmt.Sprintf("%s/.kubefirst/config", home) + + // Clone the given repository to the given directory + log.Println("git clone", url, directory) + + auth, _ := publicKey() + + auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + repo, err := git.PlainClone(directory, false, &git.CloneOptions{ + URL: url, + Auth: auth, + }) + if err != nil { + log.Println("error!, ", err) + } + + file, err := ioutil.ReadFile(fmt.Sprintf("%s/config.yaml", directory)) + if err != nil { + log.Println("error reading file", err) + } + + newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) + + err = ioutil.WriteFile(fmt.Sprintf("%s/config.yaml", directory), []byte(newFile), 0) + if err != nil { + panic(err) + } + + println("re-wrote config.yaml", home, "/.kubefirst/config") + + w, _ := repo.Worktree() + + log.Println("Committing new changes...") + w.Add(".") + w.Commit("updating soft-serve server config", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: installerEmail, + When: time.Now(), + }, + }) + + err = repo.Push(&git.PushOptions{ + RemoteName: "origin", + Auth: auth, + }) + if err != nil { + log.Println("error pushing to remote", err) + } + +} + +func pushGitopsToSoftServe() { + + directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + + // // Clone the given repository to the given directory + log.Println("open %s git repo", directory) + + repo, err := git.PlainOpen(directory) + if err != nil { + log.Println("error opening the directory ", directory, err) + } + + log.Println("git remote add origin ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops") + _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "soft", + URLs: []string{"ssh://127.0.0.1:8022/gitops"}, + }) + if err != nil { + log.Println("Error creating remote repo:", err) + os.Exit(1) + } + w, _ := repo.Worktree() + + log.Println("Committing new changes...") + w.Add(".") + w.Commit("setting new remote upstream to soft-serve", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: installerEmail, + When: time.Now(), + }, + }) + + auth, _ := publicKey() + + auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + err = repo.Push(&git.PushOptions{ + RemoteName: "soft", + Auth: auth, + }) + if err != nil { + log.Println("error pushing to remote", err) + } + +} + +func pushGitopsToGitLab() { + domain := viper.GetString("aws.domainname") + + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) + directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + + repo, err := git.PlainOpen(directory) + if err != nil { + log.Println("error opening the directory ", directory, err) + } + + //upstream := fmt.Sprintf("ssh://gitlab.%s:22:kubefirst/gitops", viper.GetString("aws.domainname")) + // upstream := "git@gitlab.kube1st.com:kubefirst/gitops.git" + upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) + log.Println("git remote add gitlab at url", upstream) + + _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "gitlab", + URLs: []string{upstream}, + }) + if err != nil { + log.Println("Error creating remote repo:", err) + } + w, _ := repo.Worktree() + + os.RemoveAll(directory + "/terraform/base/.terraform") + os.RemoveAll(directory + "/terraform/gitlab/.terraform") + os.RemoveAll(directory + "/terraform/vault/.terraform") + + log.Println("Committing new changes...") + w.Add(".") + _, err = w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: installerEmail, + When: time.Now(), + }, + }) + if err != nil { + log.Println("error committing changes", err) + } + + log.Println("setting auth...") + // auth, _ := publicKey() + // auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + auth := &gitHttp.BasicAuth{ + Username: "root", + Password: viper.GetString("gitlab.token"), + } + + err = repo.Push(&git.PushOptions{ + RemoteName: "gitlab", + Auth: auth, + }) + if err != nil { + log.Println("error pushing to remote", err) + } + +} + +func detokenize(path string) { + + err := filepath.Walk(path, detokenizeDirectory) + if err != nil { + panic(err) + } +} + +func detokenizeDirectory(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if fi.IsDir() { + return nil // + } + + if strings.Contains(path, ".git") || strings.Contains(path, ".terraform") { + return nil + } + + matched, err := filepath.Match("*", fi.Name()) + + if err != nil { + panic(err) + } + + if matched { + read, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + // todo should detokenize be a switch statement based on a value found in viper? + gitlabConfigured := viper.GetBool("gitlab.keyuploaded") + + newContents := "" + + if gitlabConfigured { + newContents = strings.Replace(string(read), "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.domainname")), -1) + } else { + newContents = strings.Replace(string(read), "https://gitlab./kubefirst/gitops.git", "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", -1) + } + + botPublicKey := viper.GetString("botpublickey") + domainId := viper.GetString("aws.domainid") + domainName := viper.GetString("aws.domainname") + bucketStateStore := viper.GetString("bucket.state-store.name") + bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") + bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") + bucketChartmuseum := viper.GetString("bucket.chartmuseum.name") + region := viper.GetString("aws.region") + adminEmail := viper.GetString("adminemail") + awsAccountId := viper.GetString("aws.accountid") + kmsKeyId := viper.GetString("vault.kmskeyid") + + newContents = strings.Replace(newContents, "", strings.TrimSpace(botPublicKey), -1) + newContents = strings.Replace(newContents, "", bucketStateStore, -1) + newContents = strings.Replace(newContents, "", bucketArgoArtifacts, -1) + newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) + newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) + newContents = strings.Replace(newContents, "", domainId, -1) + newContents = strings.Replace(newContents, "", domainName, -1) + newContents = strings.Replace(newContents, "", region, -1) + newContents = strings.Replace(newContents, "", adminEmail, -1) + newContents = strings.Replace(newContents, "", awsAccountId, -1) + if kmsKeyId != "" { + newContents = strings.Replace(newContents, "", kmsKeyId, -1) + } + + if viper.GetBool("create.terraformapplied.gitlab") { + newContents = strings.Replace(newContents, "", domainName, -1) + newContents = strings.Replace(newContents, "", region, -1) + newContents = strings.Replace(newContents, "", awsAccountId, -1) + } + + err = ioutil.WriteFile(path, []byte(newContents), 0) + if err != nil { + panic(err) + } + + } + + return nil +} + +func download() { + toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) + + err := os.Mkdir(toolsDir, 0777) + if err != nil { + log.Println("error creating directory %s", toolsDir, err) + } + + kubectlVersion := "v1.20.0" + kubectlDownloadUrl := fmt.Sprintf("https://dl.k8s.io/release/%s/bin/%s/%s/kubectl", kubectlVersion, localOs, localArchitecture) + downloadFile(kubectlClientPath, kubectlDownloadUrl) + os.Chmod(kubectlClientPath, 0755) + + // todo this kubeconfig is not available to us until we have run the terraform in base/ + os.Setenv("KUBECONFIG", kubeconfigPath) + log.Println("going to print the kubeconfig env in runtime", os.Getenv("KUBECONFIG")) + + kubectlVersionCmd := exec.Command(kubectlClientPath, "version", "--client", "--short") + kubectlVersionCmd.Stdout = os.Stdout + kubectlVersionCmd.Stderr = os.Stderr + err = kubectlVersionCmd.Run() + if err != nil { + log.Println("failed to call kubectlVersionCmd.Run(): %v", err) + } + Trackers[trackerStage5].Tracker.Increment(int64(1)) + // argocdVersion := "v2.3.4" + // argocdDownloadUrl := fmt.Sprintf("https://github.com/argoproj/argo-cd/releases/download/%s/argocd-%s-%s", argocdVersion, localOs, localArchitecture) + // argocdClientPath := fmt.Sprintf("%s/.kubefirst/tools/argocd", home) + // downloadFile(argocdClientPath, argocdDownloadUrl) + // os.Chmod(argocdClientPath, 755) + + // argocdVersionCmd := exec.Command(argocdClientPath, "version", "--client", "--short") + // argocdVersionCmd.Stdout = os.Stdout + // argocdVersionCmd.Stderr = os.Stderr + // err = argocdVersionCmd.Run() + // if err != nil { + // fmt.Println("failed to call argocdVersionCmd.Run(): %v", err) + // } + + // todo adopt latest helmVersion := "v3.9.0" + terraformVersion := "1.0.11" + // terraformClientPath := fmt.Sprintf("./%s-%s/terraform", localOs, localArchitecture) + terraformDownloadUrl := fmt.Sprintf("https://releases.hashicorp.com/terraform/%s/terraform_%s_%s_%s.zip", terraformVersion, terraformVersion, localOs, localArchitecture) + terraformDownloadZipPath := fmt.Sprintf("%s/.kubefirst/tools/terraform.zip", home) + downloadFile(terraformDownloadZipPath, terraformDownloadUrl) + // terraformZipDownload, err := os.Open(terraformDownloadZipPath) + if err != nil { + log.Println("error reading terraform file") + } + unzipDirectory := fmt.Sprintf("%s/.kubefirst/tools", home) + unzip(terraformDownloadZipPath, unzipDirectory) + + os.Chmod(unzipDirectory, 0777) + os.Chmod(fmt.Sprintf("%s/terraform", unzipDirectory), 0755) + Trackers[trackerStage5].Tracker.Increment(int64(1)) + + // todo adopt latest helmVersion := "v3.9.0" + helmVersion := "v3.2.1" + helmDownloadUrl := fmt.Sprintf("https://get.helm.sh/helm-%s-%s-%s.tar.gz", helmVersion, localOs, localArchitecture) + helmDownloadTarGzPath := fmt.Sprintf("%s/.kubefirst/tools/helm.tar.gz", home) + downloadFile(helmDownloadTarGzPath, helmDownloadUrl) + helmTarDownload, err := os.Open(helmDownloadTarGzPath) + if err != nil { + log.Println("error reading helm file") + } + extractFileFromTarGz(helmTarDownload, fmt.Sprintf("%s-%s/helm", localOs, localArchitecture), helmClientPath) + os.Chmod(helmClientPath, 0755) + helmVersionCmd := exec.Command(helmClientPath, "version", "--client", "--short") + + // currently argocd init values is generated by flare nebulous ssh + + // todo helm install argocd --create-namespace --wait --values ~/.kubefirst/argocd-init-values.yaml argo/argo-cd + helmVersionCmd.Stdout = os.Stdout + helmVersionCmd.Stderr = os.Stderr + err = helmVersionCmd.Run() + if err != nil { + log.Println("failed to call helmVersionCmd.Run(): %v", err) + } + Trackers[trackerStage5].Tracker.Increment(int64(1)) + +} + +func downloadFile(filepath string, url string) (err error) { + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Get the data + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Check server response + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("bad status: %s", resp.Status) + } + + // Writer the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + return nil +} +func extractFileFromTarGz(gzipStream io.Reader, tarAddress string, targetFilePath string) { + uncompressedStream, err := gzip.NewReader(gzipStream) + if err != nil { + log.Fatal("extractTarGz: NewReader failed") + } + + tarReader := tar.NewReader(uncompressedStream) + + for { + header, err := tarReader.Next() + if err == io.EOF { + break + } + if err != nil { + log.Println("extractTarGz: Next() failed: %s", err.Error()) + } + log.Println(header.Name) + if header.Name == tarAddress { + switch header.Typeflag { + case tar.TypeReg: + outFile, err := os.Create(targetFilePath) + if err != nil { + log.Println("extractTarGz: Create() failed: %s", err.Error()) + } + if _, err := io.Copy(outFile, tarReader); err != nil { + log.Println("extractTarGz: Copy() failed: %s", err.Error()) + } + outFile.Close() + + default: + log.Println( + "extractTarGz: uknown type: %s in %s", + header.Typeflag, + header.Name) + } + + } + } +} + +func extractTarGz(gzipStream io.Reader) { + uncompressedStream, err := gzip.NewReader(gzipStream) + if err != nil { + log.Fatal("extractTarGz: NewReader failed") + } + + tarReader := tar.NewReader(uncompressedStream) + + for { + header, err := tarReader.Next() + + if err == io.EOF { + break + } + + if err != nil { + log.Println("extractTarGz: Next() failed: %s", err.Error()) + } + + switch header.Typeflag { + case tar.TypeDir: + if err := os.Mkdir(header.Name, 0755); err != nil { + log.Println("extractTarGz: Mkdir() failed: %s", err.Error()) + } + case tar.TypeReg: + outFile, err := os.Create(header.Name) + if err != nil { + log.Println("extractTarGz: Create() failed: %s", err.Error()) + } + if _, err := io.Copy(outFile, tarReader); err != nil { + log.Println("extractTarGz: Copy() failed: %s", err.Error()) + } + outFile.Close() + + default: + log.Println( + "extractTarGz: uknown type: %s in %s", + header.Typeflag, + header.Name) + } + + } +} + +func createSoftServe(kubeconfigPath string) { + + toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) + + err := os.Mkdir(toolsDir, 0777) + if err != nil { + log.Println("error creating directory %s", toolsDir, err) + } + + // create soft-serve stateful set + softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", home) + kubectlCreateSoftServeCmd := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") + kubectlCreateSoftServeCmd.Stdout = os.Stdout + kubectlCreateSoftServeCmd.Stderr = os.Stderr + err = kubectlCreateSoftServeCmd.Run() + if err != nil { + log.Println("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) + } +} + +func helmInstallArgocd(home string, kubeconfigPath string) { + + argocdCreated := viper.GetBool("create.argocd.helm") + if !argocdCreated { + helmClientPath := fmt.Sprintf("%s/.kubefirst/tools/helm", home) + + // ! commenting out until a clean execution is necessary // create namespace + helmRepoAddArgocd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") + helmRepoAddArgocd.Stdout = os.Stdout + helmRepoAddArgocd.Stderr = os.Stderr + err := helmRepoAddArgocd.Run() + if err != nil { + log.Println("failed to call helmRepoAddArgocd.Run(): %v", err) + } + + helmRepoUpdate := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "update") + helmRepoUpdate.Stdout = os.Stdout + helmRepoUpdate.Stderr = os.Stderr + err = helmRepoUpdate.Run() + if err != nil { + log.Println("failed to call helmRepoUpdate.Run(): %v", err) + } + + helmInstallArgocdCmd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") + helmInstallArgocdCmd.Stdout = os.Stdout + helmInstallArgocdCmd.Stderr = os.Stderr + err = helmInstallArgocdCmd.Run() + if err != nil { + log.Println("failed to call helmInstallArgocdCmd.Run(): %v", err) + } + + viper.Set("create.argocd.helm", true) + err = viper.WriteConfig() + if err != nil { + log.Println(err) + } + } +} + +func createSshKeyPair() { + publicKey := viper.GetString("botpublickey") + if publicKey == "" { + log.Println("generating new key pair") + publicKey, privateKey, _ := gitlabSsh.GenerateKey() + viper.Set("botPublicKey", publicKey) + viper.Set("botPrivateKey", privateKey) + err := viper.WriteConfig() + if err != nil { + log.Println(err) + } + } + publicKey = viper.GetString("botpublickey") + privateKey := viper.GetString("botprivatekey") + + var argocdInitValuesYaml = []byte(fmt.Sprintf(` +server: + additionalApplications: + - name: registry + namespace: argocd + additionalLabels: {} + additionalAnnotations: {} + finalizers: + - resources-finalizer.argocd.argoproj.io + project: default + source: + repoURL: ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops + targetRevision: HEAD + path: registry + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true +configs: + repositories: + soft-serve-gitops: + url: ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops + insecure: 'true' + type: git + name: soft-serve-gitops + credentialTemplates: + ssh-creds: + url: ssh://soft-serve.soft-serve.svc.cluster.local:22 + sshPrivateKey: | + %s +`, strings.ReplaceAll(privateKey, "\n", "\n "))) + + // fmt.Println("argo init vals:\n", string(argocdInitValuesYaml)) + + err := ioutil.WriteFile(fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), argocdInitValuesYaml, 0644) + if err != nil { + log.Println("received an error while writing the argocd-init-values.yaml file", err.Error()) + panic("error: argocd-init-values.yaml" + err.Error()) + } +} + +func unzip(zipFilepath string, unzipDirectory string) { + dst := unzipDirectory + archive, err := zip.OpenReader(zipFilepath) + if err != nil { + panic(err) + } + defer archive.Close() + + for _, f := range archive.File { + filePath := filepath.Join(dst, f.Name) + log.Println("unzipping file ", filePath) + + if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) { + log.Println("invalid file path") + return + } + if f.FileInfo().IsDir() { + log.Println("creating directory...") + os.MkdirAll(filePath, os.ModePerm) + continue + } + + if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { + panic(err) + } + + dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + panic(err) + } + + fileInArchive, err := f.Open() + if err != nil { + panic(err) + } + + if _, err := io.Copy(dstFile, fileInArchive); err != nil { + panic(err) + } + + dstFile.Close() + fileInArchive.Close() + } +} From 171bd5d381a2578d67a83c07cd578e8cdc8750d0 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 29 Jun 2022 16:37:23 +0000 Subject: [PATCH 005/107] adding globals note Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 cmd/README.md diff --git a/cmd/README.md b/cmd/README.md new file mode 100644 index 000000000..ccaac1ea8 --- /dev/null +++ b/cmd/README.md @@ -0,0 +1,34 @@ +# Overview + +General overview of the code, to help shuffling parts around. + +# Globals Variables + +| Variable|Type | Use | +|:--|:--|:--| +|cleanCmd|Command| Clean command| +|createCmd|Command| Create command| +|destroyCmd|Command| Destroy command| +|nebulousCmd|Command| CLI main command | +|versionCmd|Command| Version command| +|rootCmd|Command| Root command command| +|infoCmd|Command| Info command| +|initCmd|Command| Init command - pre-provision command| +|home|String|User Home dir or Work dir| +|kubectlClientPath|String|Kubectl CLI path| +|kubeconfigPath|String|Kubeconfig Path| +|localOs|String|Host OS| +|localArchitecture|String|Host OS architecture| +|terraformPath|String|Terraform CLI path| +|helmClientPath|String|Helm CLI path| +|dryrunMode|Bool|If installer should run in dry-run mode| +|Trackers|Trecker|Map of trackers| +|vaultRootToken|String|Root token for vault| +|gitlabToolboxPodName|String|Toolbox pod name| +|gitlabSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| +|vaultSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| +|argocdSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| +|gitlabSecretClient|coreV1Types.PodInterface|Client shorthand to interface| +|cfgFile|String| .flare config file| +|NebolousVersion|String|CLI version| + From 9d7a5a7d3451953597dc2e40522b6ca0d1809a91 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 30 Jun 2022 13:02:35 +0000 Subject: [PATCH 006/107] removing the templates Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- gitops/.gitignore | 10 - gitops/LICENSE | 21 - gitops/README.md | 59 - gitops/atlantis.yaml | 28 - gitops/components/argo/argo.yaml | 730 ---- gitops/components/argo/cwfts.yaml | 22 - .../argo/cwfts/configmap-nodejs.yaml | 38 - gitops/components/argo/cwfts/cwft-ci.yaml | 17 - gitops/components/argo/cwfts/cwft-docker.yaml | 50 - gitops/components/argo/cwfts/cwft-git.yaml | 103 - gitops/components/argo/cwfts/cwft-helm.yaml | 190 - gitops/components/argo/cwfts/cwft-npm.yaml | 46 - gitops/components/argo/externalsecrets.yaml | 65 - gitops/components/argo/ingress.yaml | 21 - gitops/components/argocd/configmap.yaml | 26 - gitops/components/argocd/ingress.yaml | 24 - gitops/components/atlantis/application.yaml | 69 - gitops/components/atlantis/configmap.yaml | 34 - .../components/atlantis/externalsecret.yaml | 204 - .../components/cert-issuers/letsencrypt.yaml | 29 - .../components/chartmuseum/application.yaml | 35 - .../chartmuseum/externalsecret.yaml | 28 - gitops/components/chartmuseum/ingress.yaml | 30 - gitops/components/development/metaphor.yaml | 27 - .../development/metaphor/Chart.yaml | 9 - .../development/metaphor/values.yaml | 16 - .../auth-delegator-crb.yaml | 12 - .../external-secrets-operator.yaml | 40 - .../vault-cluster-secret-store-secret.yaml | 20 - .../components/gitlab-runner/application.yaml | 49 - .../gitlab-runner/externalsecret.yaml | 20 - gitops/components/production/metaphor.yaml | 27 - .../components/production/metaphor/Chart.yaml | 9 - .../production/metaphor/values.yaml | 16 - gitops/components/staging/metaphor.yaml | 27 - gitops/components/staging/metaphor/Chart.yaml | 9 - .../components/staging/metaphor/values.yaml | 16 - gitops/logo.png | Bin 78955 -> 0 bytes gitops/registry/argo.yaml | 79 - gitops/registry/argocd.yaml | 22 - gitops/registry/atlantis.yaml | 24 - gitops/registry/cert-issuers.yaml | 24 - gitops/registry/cert-manager.yaml | 25 - gitops/registry/chartmuseum.yaml | 24 - gitops/registry/development.yaml | 24 - gitops/registry/external-dns.yaml | 29 - .../registry/external-secrets-operator.yaml | 24 - gitops/registry/external-secrets-store.yaml | 22 - gitops/registry/gitlab-runner.yaml | 24 - gitops/registry/ingress-nginx.yaml | 35 - gitops/registry/production.yaml | 24 - gitops/registry/staging.yaml | 24 - gitops/registry/vault.yaml | 118 - gitops/terraform/argocd/backend.tf | 8 - .../argocd/internal-repos/provider.tf | 10 - .../terraform/argocd/internal-repos/repos.tf | 7 - gitops/terraform/argocd/main.tf | 16 - gitops/terraform/argocd/registry/provider.tf | 10 - gitops/terraform/argocd/registry/registry.tf | 7 - gitops/terraform/argocd/repos/provider.tf | 10 - gitops/terraform/argocd/repos/repos.tf | 77 - .../argocd/templates/argocd-app/main.tf | 88 - gitops/terraform/base/cypress/.DS_Store | Bin 6148 -> 0 bytes gitops/terraform/base/cypress/cypress.json | 5 - .../terraform/base/cypress/cypress/.DS_Store | Bin 6148 -> 0 bytes .../cypress/cypress/fixtures/example.json | 5 - .../cypress/integration/gitlab-init.spec.js | 40 - .../base/cypress/cypress/plugins/index.js | 21 - .../base/cypress/cypress/support/commands.js | 25 - .../base/cypress/cypress/support/index.js | 29 - .../terraform/base/cypress/package-lock.json | 1578 -------- gitops/terraform/base/cypress/package.json | 15 - gitops/terraform/base/dynamodb/output.tf | 3 - gitops/terraform/base/dynamodb/vault.tf | 83 - gitops/terraform/base/ec2/gitlab-vm.tf | 83 - gitops/terraform/base/ec2/outputs.tf | 3 - .../base/ec2/scripts/install_gitlab.sh | 16 - gitops/terraform/base/ec2/variables.tf | 31 - gitops/terraform/base/eks/main.tf | 267 -- gitops/terraform/base/eks/outputs.tf | 35 - gitops/terraform/base/eks/variables.tf | 48 - gitops/terraform/base/kms/main.tf | 98 - gitops/terraform/base/main.tf | 61 - gitops/terraform/base/outputs.tf | 10 - gitops/terraform/base/s3/main.tf | 27 - .../base/security-groups/gitlab-sg.tf | 72 - .../terraform/base/security-groups/outputs.tf | 3 - .../base/security-groups/variables.tf | 4 - gitops/terraform/base/variables.tf | 27 - gitops/terraform/cypress/cypress.json | 5 - .../cypress/cypress/fixtures/example.json | 5 - .../cypress/integration/gitlab-init.spec.js | 41 - .../cypress/cypress/plugins/index.js | 21 - .../cypress/cypress/support/commands.js | 25 - .../cypress/cypress/support/index.js | 29 - gitops/terraform/cypress/package-lock.json | 1523 -------- gitops/terraform/cypress/package.json | 15 - gitops/terraform/gitlab/kubefirst-repos.tf | 53 - .../gitlab/templates/gitlab-repo/main.tf | 27 - .../gitlab/templates/gitlab-repo/outputs.tf | 4 - .../gitlab/templates/gitlab-repo/variables.tf | 53 - .../gitlab/templates/gitlab-repo/versions.tf | 10 - gitops/terraform/gitlab/versions.tf | 7 - gitops/terraform/users/admin.tf | 25 - gitops/terraform/users/backend.tf | 8 - gitops/terraform/users/developers.tf | 23 - .../users/templates/oidc-user/user.tf | 70 - .../users/templates/oidc-user/versions.tf | 14 - gitops/terraform/users/versions.tf | 7 - .../vault/bootstrap/k8s-auth-backend.tf | 57 - gitops/terraform/vault/bootstrap/kv-mounts.tf | 13 - gitops/terraform/vault/bootstrap/policies.tf | 237 -- gitops/terraform/vault/bootstrap/secrets.tf | 167 - gitops/terraform/vault/bootstrap/variables.tf | 57 - gitops/terraform/vault/main.tf | 38 - gitops/terraform/vault/oidc/main.tf | 46 - gitops/terraform/vault/variables.tf | 66 - metaphor/.argo/ci-files/trigger.txt | 1 - metaphor/.argo/deploy.yaml | 89 - metaphor/.argo/npm-run.yaml | 59 - metaphor/.argo/publish.yaml | 102 - metaphor/.argo/release.yaml | 159 - metaphor/.gitignore | 3 - metaphor/.gitlab-ci.yml | 122 - metaphor/Dockerfile | 24 - metaphor/LICENSE | 21 - metaphor/README.md | 36 - metaphor/charts/metaphor/.helmignore | 22 - metaphor/charts/metaphor/Chart.yaml | 6 - metaphor/charts/metaphor/templates/NOTES.txt | 21 - .../charts/metaphor/templates/_helpers.tpl | 63 - metaphor/charts/metaphor/templates/cm.yaml | 9 - .../charts/metaphor/templates/deployment.yaml | 75 - .../metaphor/templates/external-secrets.yaml | 22 - .../charts/metaphor/templates/ingress.yaml | 41 - .../charts/metaphor/templates/service.yaml | 15 - .../metaphor/templates/serviceaccount.yaml | 9 - .../templates/tests/test-connection.yaml | 15 - metaphor/charts/metaphor/values.yaml | 71 - metaphor/logo.png | Bin 53897 -> 0 bytes metaphor/nodemon.json | 7 - metaphor/package-lock.json | 3327 ----------------- metaphor/package.json | 49 - metaphor/src/app.ts | 57 - metaphor/src/server.ts | 28 - metaphor/src/utils/logger.ts | 8 - metaphor/src/utils/tracer.ts | 32 - metaphor/src/views/home.ts | 26 - metaphor/src/views/index.pug | 23 - metaphor/src/views/style.css | 23 - metaphor/tsconfig.json | 22 - metaphor/tslint.json | 46 - metaphor/views/docker.png | Bin 22767 -> 0 bytes metaphor/views/index.pug | 23 - metaphor/views/style.css | 23 - terraform/main.tf | 1 - 156 files changed, 12846 deletions(-) delete mode 100644 gitops/.gitignore delete mode 100644 gitops/LICENSE delete mode 100644 gitops/README.md delete mode 100644 gitops/atlantis.yaml delete mode 100644 gitops/components/argo/argo.yaml delete mode 100644 gitops/components/argo/cwfts.yaml delete mode 100644 gitops/components/argo/cwfts/configmap-nodejs.yaml delete mode 100644 gitops/components/argo/cwfts/cwft-ci.yaml delete mode 100644 gitops/components/argo/cwfts/cwft-docker.yaml delete mode 100644 gitops/components/argo/cwfts/cwft-git.yaml delete mode 100644 gitops/components/argo/cwfts/cwft-helm.yaml delete mode 100644 gitops/components/argo/cwfts/cwft-npm.yaml delete mode 100644 gitops/components/argo/externalsecrets.yaml delete mode 100644 gitops/components/argo/ingress.yaml delete mode 100644 gitops/components/argocd/configmap.yaml delete mode 100644 gitops/components/argocd/ingress.yaml delete mode 100644 gitops/components/atlantis/application.yaml delete mode 100644 gitops/components/atlantis/configmap.yaml delete mode 100644 gitops/components/atlantis/externalsecret.yaml delete mode 100644 gitops/components/cert-issuers/letsencrypt.yaml delete mode 100644 gitops/components/chartmuseum/application.yaml delete mode 100644 gitops/components/chartmuseum/externalsecret.yaml delete mode 100644 gitops/components/chartmuseum/ingress.yaml delete mode 100644 gitops/components/development/metaphor.yaml delete mode 100644 gitops/components/development/metaphor/Chart.yaml delete mode 100644 gitops/components/development/metaphor/values.yaml delete mode 100644 gitops/components/external-secrets-operator/auth-delegator-crb.yaml delete mode 100644 gitops/components/external-secrets-operator/external-secrets-operator.yaml delete mode 100644 gitops/components/external-secrets-store/vault-cluster-secret-store-secret.yaml delete mode 100644 gitops/components/gitlab-runner/application.yaml delete mode 100644 gitops/components/gitlab-runner/externalsecret.yaml delete mode 100644 gitops/components/production/metaphor.yaml delete mode 100644 gitops/components/production/metaphor/Chart.yaml delete mode 100644 gitops/components/production/metaphor/values.yaml delete mode 100644 gitops/components/staging/metaphor.yaml delete mode 100644 gitops/components/staging/metaphor/Chart.yaml delete mode 100644 gitops/components/staging/metaphor/values.yaml delete mode 100644 gitops/logo.png delete mode 100644 gitops/registry/argo.yaml delete mode 100644 gitops/registry/argocd.yaml delete mode 100644 gitops/registry/atlantis.yaml delete mode 100644 gitops/registry/cert-issuers.yaml delete mode 100644 gitops/registry/cert-manager.yaml delete mode 100644 gitops/registry/chartmuseum.yaml delete mode 100644 gitops/registry/development.yaml delete mode 100644 gitops/registry/external-dns.yaml delete mode 100644 gitops/registry/external-secrets-operator.yaml delete mode 100644 gitops/registry/external-secrets-store.yaml delete mode 100644 gitops/registry/gitlab-runner.yaml delete mode 100644 gitops/registry/ingress-nginx.yaml delete mode 100644 gitops/registry/production.yaml delete mode 100644 gitops/registry/staging.yaml delete mode 100644 gitops/registry/vault.yaml delete mode 100644 gitops/terraform/argocd/backend.tf delete mode 100644 gitops/terraform/argocd/internal-repos/provider.tf delete mode 100644 gitops/terraform/argocd/internal-repos/repos.tf delete mode 100644 gitops/terraform/argocd/main.tf delete mode 100644 gitops/terraform/argocd/registry/provider.tf delete mode 100644 gitops/terraform/argocd/registry/registry.tf delete mode 100644 gitops/terraform/argocd/repos/provider.tf delete mode 100644 gitops/terraform/argocd/repos/repos.tf delete mode 100644 gitops/terraform/argocd/templates/argocd-app/main.tf delete mode 100644 gitops/terraform/base/cypress/.DS_Store delete mode 100644 gitops/terraform/base/cypress/cypress.json delete mode 100644 gitops/terraform/base/cypress/cypress/.DS_Store delete mode 100644 gitops/terraform/base/cypress/cypress/fixtures/example.json delete mode 100644 gitops/terraform/base/cypress/cypress/integration/gitlab-init.spec.js delete mode 100644 gitops/terraform/base/cypress/cypress/plugins/index.js delete mode 100644 gitops/terraform/base/cypress/cypress/support/commands.js delete mode 100644 gitops/terraform/base/cypress/cypress/support/index.js delete mode 100644 gitops/terraform/base/cypress/package-lock.json delete mode 100644 gitops/terraform/base/cypress/package.json delete mode 100644 gitops/terraform/base/dynamodb/output.tf delete mode 100644 gitops/terraform/base/dynamodb/vault.tf delete mode 100644 gitops/terraform/base/ec2/gitlab-vm.tf delete mode 100644 gitops/terraform/base/ec2/outputs.tf delete mode 100755 gitops/terraform/base/ec2/scripts/install_gitlab.sh delete mode 100644 gitops/terraform/base/ec2/variables.tf delete mode 100644 gitops/terraform/base/eks/main.tf delete mode 100644 gitops/terraform/base/eks/outputs.tf delete mode 100644 gitops/terraform/base/eks/variables.tf delete mode 100644 gitops/terraform/base/kms/main.tf delete mode 100644 gitops/terraform/base/main.tf delete mode 100644 gitops/terraform/base/outputs.tf delete mode 100644 gitops/terraform/base/s3/main.tf delete mode 100644 gitops/terraform/base/security-groups/gitlab-sg.tf delete mode 100644 gitops/terraform/base/security-groups/outputs.tf delete mode 100644 gitops/terraform/base/security-groups/variables.tf delete mode 100644 gitops/terraform/base/variables.tf delete mode 100644 gitops/terraform/cypress/cypress.json delete mode 100644 gitops/terraform/cypress/cypress/fixtures/example.json delete mode 100644 gitops/terraform/cypress/cypress/integration/gitlab-init.spec.js delete mode 100644 gitops/terraform/cypress/cypress/plugins/index.js delete mode 100644 gitops/terraform/cypress/cypress/support/commands.js delete mode 100644 gitops/terraform/cypress/cypress/support/index.js delete mode 100644 gitops/terraform/cypress/package-lock.json delete mode 100644 gitops/terraform/cypress/package.json delete mode 100644 gitops/terraform/gitlab/kubefirst-repos.tf delete mode 100644 gitops/terraform/gitlab/templates/gitlab-repo/main.tf delete mode 100644 gitops/terraform/gitlab/templates/gitlab-repo/outputs.tf delete mode 100644 gitops/terraform/gitlab/templates/gitlab-repo/variables.tf delete mode 100644 gitops/terraform/gitlab/templates/gitlab-repo/versions.tf delete mode 100644 gitops/terraform/gitlab/versions.tf delete mode 100644 gitops/terraform/users/admin.tf delete mode 100644 gitops/terraform/users/backend.tf delete mode 100644 gitops/terraform/users/developers.tf delete mode 100644 gitops/terraform/users/templates/oidc-user/user.tf delete mode 100644 gitops/terraform/users/templates/oidc-user/versions.tf delete mode 100644 gitops/terraform/users/versions.tf delete mode 100644 gitops/terraform/vault/bootstrap/k8s-auth-backend.tf delete mode 100644 gitops/terraform/vault/bootstrap/kv-mounts.tf delete mode 100644 gitops/terraform/vault/bootstrap/policies.tf delete mode 100644 gitops/terraform/vault/bootstrap/secrets.tf delete mode 100644 gitops/terraform/vault/bootstrap/variables.tf delete mode 100644 gitops/terraform/vault/main.tf delete mode 100644 gitops/terraform/vault/oidc/main.tf delete mode 100644 gitops/terraform/vault/variables.tf delete mode 100644 metaphor/.argo/ci-files/trigger.txt delete mode 100644 metaphor/.argo/deploy.yaml delete mode 100644 metaphor/.argo/npm-run.yaml delete mode 100644 metaphor/.argo/publish.yaml delete mode 100644 metaphor/.argo/release.yaml delete mode 100644 metaphor/.gitignore delete mode 100644 metaphor/.gitlab-ci.yml delete mode 100644 metaphor/Dockerfile delete mode 100644 metaphor/LICENSE delete mode 100644 metaphor/README.md delete mode 100644 metaphor/charts/metaphor/.helmignore delete mode 100644 metaphor/charts/metaphor/Chart.yaml delete mode 100644 metaphor/charts/metaphor/templates/NOTES.txt delete mode 100644 metaphor/charts/metaphor/templates/_helpers.tpl delete mode 100644 metaphor/charts/metaphor/templates/cm.yaml delete mode 100644 metaphor/charts/metaphor/templates/deployment.yaml delete mode 100644 metaphor/charts/metaphor/templates/external-secrets.yaml delete mode 100644 metaphor/charts/metaphor/templates/ingress.yaml delete mode 100644 metaphor/charts/metaphor/templates/service.yaml delete mode 100644 metaphor/charts/metaphor/templates/serviceaccount.yaml delete mode 100644 metaphor/charts/metaphor/templates/tests/test-connection.yaml delete mode 100644 metaphor/charts/metaphor/values.yaml delete mode 100644 metaphor/logo.png delete mode 100644 metaphor/nodemon.json delete mode 100644 metaphor/package-lock.json delete mode 100644 metaphor/package.json delete mode 100644 metaphor/src/app.ts delete mode 100644 metaphor/src/server.ts delete mode 100644 metaphor/src/utils/logger.ts delete mode 100644 metaphor/src/utils/tracer.ts delete mode 100644 metaphor/src/views/home.ts delete mode 100644 metaphor/src/views/index.pug delete mode 100644 metaphor/src/views/style.css delete mode 100644 metaphor/tsconfig.json delete mode 100644 metaphor/tslint.json delete mode 100644 metaphor/views/docker.png delete mode 100644 metaphor/views/index.pug delete mode 100644 metaphor/views/style.css delete mode 100644 terraform/main.tf diff --git a/gitops/.gitignore b/gitops/.gitignore deleted file mode 100644 index 8f16ace9a..000000000 --- a/gitops/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -*.env -node_modules -.terraform -.gitlab-bot-access-token -.gitlab-runner-registration-token -.vscode -terraform-ssh-key -*/cypress/screenshots/ -*/cypress/videos/ -.DS_Store diff --git a/gitops/LICENSE b/gitops/LICENSE deleted file mode 100644 index 870c5e8f5..000000000 --- a/gitops/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 kubefirst - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/gitops/README.md b/gitops/README.md deleted file mode 100644 index 5059b95b9..000000000 --- a/gitops/README.md +++ /dev/null @@ -1,59 +0,0 @@ -![](logo.png) - -# gitops - -The `gitops` repository has 2 main section - -- `/registry`: the argocd gitops app registry -- `/terraform`: infrastructure as code & configuration as code - -## kubefirst apps - -The [kubefirst/nebulous](https://hub.docker.com/repository/docker/kubefirst/nebulous) installation has established the following applications: - -| Application | Namespace | Description | URL (where applicable) | -|--------------------------|------------------|---------------------------------------------|-----------------------------------------------------| -| GitLab | | Privately Hosted GitLab Omnibus Server | https://gitlab. | -| Vault | vault | Secrets Management | https://vault. | -| Argo CD | argocd | GitOps Continuous Delivery | https://argocd. | -| Argo Workflows | argo | Application Continuous Integration | https://argo. | -| Keycloak | keycloak | Authentication | https://keycloak. | -| Atlantis | atlantis | Terraform Workflow Automation | https://atlantis. | -| Chart Museum | chartmuseum | Helm Chart Registry | https://chartmuseum. | -| Metaphor Development | development | Development instance of sample application | https://metaphor-development. | -| Metaphor Staging | staging | Staging instance of sample application | https://metaphor-staging. | -| Metaphor Production | production | Production instance of sample application | https://metaphor-production. | -| Nginx Ingress Controller | ingress-nginx | Ingress Controller | | -| Cert Manager | cert-manager | Certificate Automation Utility | | -| Certificate Issuers | clusterwide | Let's Encrypt browser-trusted certificates | | -| External Secrets | external-secrets | Syncs Kubernetes secrets with Vault secrets | | -| GitLab Runner | gitlab-runner | GitLab CI Executor | | - -## argocd registry - -The argocd configurations in this repo can be found in the [registry directory](./registry). The applications that you build and release on the kubefirst platform will also be registered here in the development, staging, and production folders. The `metaphor` app can be found there to serve as an example to follow. - -The `main` branch of this repo represents the desired state all apps registered with kubernetes. Argo CD will automatically try to converge your desired state with the actual state in kubernetes with a process called Argo Sync. You can see the Sync status of all of your apps in the [argo cd ui](https://argo.). - -## terraform infrastructure as code - -The terraform in this repository can be found in the `/terraform` directory. - -All of our terraform is automated with atlantis. To see the terraform entry points and under what circumstance they are triggered, see [atlantis.yaml](./atlantis.yaml). - -Any change to a `*.tf` file, even a whitespace change, will trigger its corresponding Atlantis workflow once a merge request is submitted in GitLab. Within a minute it will post the plan to the pull request with instruction on how to apply the plan if approved. - -## terraform configuration as code - -In addition to infrastructure terraform, the `gitops` repository also contains configuration as code for the following products: -- ArgoCD: The Argo CD app-registry, repositories, and secrets -- GitLab: Gitlab Repositories and ECR registries needed to house containers for those repositories -- Keycloak: Kubefirst Realm, User Groups for Admin and Developer roles, and sample users that get SSO access to vault, argo cd, argo workflows, and gitlab -- Vault: auth backends, secrets engine, infrastructure secrets - -## engineering onboarding - -Your kubefirst platform comes with some terraform in place for managing [admins](./terraform/keycloak/admins.tf) and [developers](./terraform/keycloak/developers.tf). At the top of these two files, you'll find a list of sample admins and developers. Replace this list with the list of actual users you want added to the admin and developer groups and open a pull request. The pull request will show you the user changes in the terraform plan. When approved, have atlantis apply the plan with an `atlantis apply` comment in the pull request. - -Your new users will have temporary passwords generated for them and stored in Vault in the `/users` secret store. - diff --git a/gitops/atlantis.yaml b/gitops/atlantis.yaml deleted file mode 100644 index cb81506a8..000000000 --- a/gitops/atlantis.yaml +++ /dev/null @@ -1,28 +0,0 @@ -version: 3 -automerge: true -projects: - - dir: terraform/argocd - terraform_version: 0.13.5 - autoplan: - enabled: true - when_modified: ["./**/*.tf", "*.tf*"] - - dir: terraform/base - terraform_version: 0.13.5 - autoplan: - enabled: true - when_modified: ["./**/*.tf", "*.tf*"] - - dir: terraform/gitlab - terraform_version: 0.13.5 - autoplan: - enabled: true - when_modified: ["./**/*.tf", "*.tf*"] - - dir: terraform/users - terraform_version: 0.13.5 - autoplan: - enabled: true - when_modified: ["./**/*.tf", "*.tf*"] - - dir: terraform/vault - terraform_version: 0.13.5 - autoplan: - enabled: true - when_modified: ["./**/*.tf", "*.tf*"] diff --git a/gitops/components/argo/argo.yaml b/gitops/components/argo/argo.yaml deleted file mode 100644 index e5dfe5a6f..000000000 --- a/gitops/components/argo/argo.yaml +++ /dev/null @@ -1,730 +0,0 @@ -# from source: https://github.com/argoproj/argo-workflows/releases/download/v3.1.11/install.yaml -# Adjustments: -# Added gitlab-runner service account to cluster role binding -# Added content to workflow-controller-configmap -# Adjusted/Added 3 argo service accounts: argo-admin, argo-server, argo-developer -# Adjusted args in server startup in deployment - -# This is an auto-generated file. DO NOT EDIT -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: clusterworkflowtemplates.argoproj.io -spec: - group: argoproj.io - names: - kind: ClusterWorkflowTemplate - listKind: ClusterWorkflowTemplateList - plural: clusterworkflowtemplates - shortNames: - - clusterwftmpl - - cwft - singular: clusterworkflowtemplate - scope: Cluster - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - type: object - x-kubernetes-map-type: atomic - x-kubernetes-preserve-unknown-fields: true - required: - - metadata - - spec - type: object - served: true - storage: true ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: cronworkflows.argoproj.io -spec: - group: argoproj.io - names: - kind: CronWorkflow - listKind: CronWorkflowList - plural: cronworkflows - shortNames: - - cwf - - cronwf - singular: cronworkflow - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - type: object - x-kubernetes-map-type: atomic - x-kubernetes-preserve-unknown-fields: true - status: - type: object - x-kubernetes-map-type: atomic - x-kubernetes-preserve-unknown-fields: true - required: - - metadata - - spec - type: object - served: true - storage: true ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: workfloweventbindings.argoproj.io -spec: - group: argoproj.io - names: - kind: WorkflowEventBinding - listKind: WorkflowEventBindingList - plural: workfloweventbindings - shortNames: - - wfeb - singular: workfloweventbinding - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - type: object - x-kubernetes-map-type: atomic - x-kubernetes-preserve-unknown-fields: true - required: - - metadata - - spec - type: object - served: true - storage: true ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: workflows.argoproj.io -spec: - group: argoproj.io - names: - kind: Workflow - listKind: WorkflowList - plural: workflows - shortNames: - - wf - singular: workflow - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Status of the workflow - jsonPath: .status.phase - name: Status - type: string - - description: When the workflow was started - format: date-time - jsonPath: .status.startedAt - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - type: object - x-kubernetes-map-type: atomic - x-kubernetes-preserve-unknown-fields: true - status: - type: object - x-kubernetes-map-type: atomic - x-kubernetes-preserve-unknown-fields: true - required: - - metadata - - spec - type: object - served: true - storage: true - subresources: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: workflowtemplates.argoproj.io -spec: - group: argoproj.io - names: - kind: WorkflowTemplate - listKind: WorkflowTemplateList - plural: workflowtemplates - shortNames: - - wftmpl - singular: workflowtemplate - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - properties: - apiVersion: - type: string - kind: - type: string - metadata: - type: object - spec: - type: object - x-kubernetes-map-type: atomic - x-kubernetes-preserve-unknown-fields: true - required: - - metadata - - spec - type: object - served: true - storage: true ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: argo ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: argo-admin - annotations: - # The rule is an expression used to determine if this service account - # should be used. - # * `groups` - an array of the OIDC groups - # * `iss` - the issuer ("argo-server") - # * `sub` - the subject (typically the username) - # Must evaluate to a boolean. - # If you want an account to be the default to use, this rule can be "true". - # Details of the expression language are available in - # https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md. - workflows.argoproj.io/rbac-rule: "'admins' in groups" - # The precedence is used to determine which service account to use whe - # Precedence is an integer. It may be negative. If omitted, it defaults to "0". - # Numerically higher values have higher precedence (not lower, which maybe - # counter-intuitive to you). - # If two rules match and have the same precedence, then which one used will - # be arbitrary. - workflows.argoproj.io/rbac-rule-precedence: "1" ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: argo-server - annotations: - workflows.argoproj.io/rbac-rule: "true" - workflows.argoproj.io/rbac-rule-precedence: "2" ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: argo-developer - annotations: - workflows.argoproj.io/rbac-rule: "'developers' in groups" - workflows.argoproj.io/rbac-rule-precedence: "0" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: argo-role -rules: -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - update -- apiGroups: - - "" - resources: - - secrets - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - rbac.authorization.k8s.io/aggregate-to-admin: "true" - name: argo-aggregate-to-admin -rules: -- apiGroups: - - argoproj.io - resources: - - workflows - - workflows/finalizers - - workfloweventbindings - - workfloweventbindings/finalizers - - workflowtemplates - - workflowtemplates/finalizers - - cronworkflows - - cronworkflows/finalizers - - clusterworkflowtemplates - - clusterworkflowtemplates/finalizers - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - rbac.authorization.k8s.io/aggregate-to-edit: "true" - name: argo-aggregate-to-edit -rules: -- apiGroups: - - argoproj.io - resources: - - workflows - - workflows/finalizers - - workfloweventbindings - - workfloweventbindings/finalizers - - workflowtemplates - - workflowtemplates/finalizers - - cronworkflows - - cronworkflows/finalizers - - clusterworkflowtemplates - - clusterworkflowtemplates/finalizers - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - rbac.authorization.k8s.io/aggregate-to-view: "true" - name: argo-aggregate-to-view -rules: -- apiGroups: - - argoproj.io - resources: - - workflows - - workflows/finalizers - - workfloweventbindings - - workfloweventbindings/finalizers - - workflowtemplates - - workflowtemplates/finalizers - - cronworkflows - - cronworkflows/finalizers - - clusterworkflowtemplates - - clusterworkflowtemplates/finalizers - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: argo-cluster-role -rules: -- apiGroups: - - "" - resources: - - pods - - pods/exec - verbs: - - create - - get - - list - - watch - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - watch - - list -- apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - create - - delete - - get -- apiGroups: - - argoproj.io - resources: - - workflows - - workflows/finalizers - verbs: - - get - - list - - watch - - update - - patch - - delete - - create -- apiGroups: - - argoproj.io - resources: - - workflowtemplates - - workflowtemplates/finalizers - - clusterworkflowtemplates - - clusterworkflowtemplates/finalizers - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - get - - list -- apiGroups: - - argoproj.io - resources: - - cronworkflows - - cronworkflows/finalizers - verbs: - - get - - list - - watch - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - create - - get - - delete ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: argo-server-cluster-role -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - watch - - list -- apiGroups: - - "" - resources: - - secrets - verbs: - - get - - create -- apiGroups: - - "" - resources: - - pods - - pods/exec - - pods/log - verbs: - - get - - list - - watch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - watch - - create - - patch -- apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - get - - list -- apiGroups: - - argoproj.io - resources: - - eventsources - - sensors - - workflows - - workfloweventbindings - - workflowtemplates - - cronworkflows - - clusterworkflowtemplates - verbs: - - create - - get - - list - - watch - - update - - patch - - delete ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: argo-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: argo-role -subjects: -- kind: ServiceAccount - name: argo ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: argo-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: argo-cluster-role -subjects: -- kind: ServiceAccount - name: argo - namespace: argo ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: argo-server-binding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: argo-server-cluster-role -subjects: -- kind: ServiceAccount - name: argo-server - namespace: argo -- kind: ServiceAccount - name: default - namespace: gitlab-runner ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: workflow-controller-configmap - -data: - artifactRepository: | - s3: - bucket: - keyFormat: "argo/artifacts\ - /{{workflow.creationTimestamp.Y}}\ - /{{workflow.creationTimestamp.m}}\ - /{{workflow.creationTimestamp.d}}\ - /{{workflow.uid}}\ - /{{workflow.name}}\ - /{{pod.name}}" - endpoint: s3.amazonaws.com - accessKeySecret: - name: ci-secrets - key: AWS_ACCESS_KEY_ID - secretKeySecret: - name: ci-secrets - key: AWS_SECRET_ACCESS_KEY - sso: | - # TODO: might need a route added - issuer: https://gitlab. - clientId: - name: argo-secrets - key: client-id - clientSecret: - name: argo-secrets - key: client-secret - # This is the redirect URL supplied to the provider (required). It must - # be in the form /oauth2/callback. It must be - # browser-accessible. - redirectUrl: https://argo./oauth2/callback - # Additional scopes to request. Typically needed for SSO RBAC. >= v2.12 - scopes: - - email - - openid - # RBAC Config. >= v2.12 - rbac: - enabled: true ---- -apiVersion: v1 -kind: Service -metadata: - name: argo-server -spec: - ports: - - name: web - port: 2746 - targetPort: 2746 - selector: - app: argo-server ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: workflow-controller - name: workflow-controller-metrics -spec: - ports: - - name: metrics - port: 9090 - protocol: TCP - targetPort: 9090 - selector: - app: workflow-controller ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: argo-server -spec: - selector: - matchLabels: - app: argo-server - template: - metadata: - labels: - app: argo-server - spec: - containers: - - args: - - server - - --secure - - --auth-mode - - sso - - --auth-mode - - client - image: docker.io/argoproj/argocli:v3.1.11 - name: argo-server - ports: - - containerPort: 2746 - name: web - readinessProbe: - httpGet: - path: / - port: 2746 - scheme: HTTPS - initialDelaySeconds: 10 - periodSeconds: 20 - securityContext: - capabilities: - drop: - - ALL - volumeMounts: - - mountPath: /tmp - name: tmp - nodeSelector: - kubernetes.io/os: linux - securityContext: - runAsNonRoot: true - serviceAccountName: argo-server - volumes: - - emptyDir: {} - name: tmp ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: workflow-controller -spec: - selector: - matchLabels: - app: workflow-controller - template: - metadata: - labels: - app: workflow-controller - spec: - containers: - - args: - - --configmap - - workflow-controller-configmap - - --executor-image - - argoproj/argoexec:v3.1.11 - command: - - workflow-controller - env: - - name: LEADER_ELECTION_IDENTITY - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: metadata.name - image: docker.io/argoproj/workflow-controller:v3.1.11 - livenessProbe: - failureThreshold: 3 - httpGet: - path: /healthz - port: 6060 - initialDelaySeconds: 90 - periodSeconds: 60 - timeoutSeconds: 30 - name: workflow-controller - ports: - - containerPort: 9090 - name: metrics - - containerPort: 6060 - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - nodeSelector: - kubernetes.io/os: linux - securityContext: - runAsNonRoot: true - serviceAccountName: argo diff --git a/gitops/components/argo/cwfts.yaml b/gitops/components/argo/cwfts.yaml deleted file mode 100644 index 94223d202..000000000 --- a/gitops/components/argo/cwfts.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: argo-workflows-cwfts - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/argo/cwfts - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: argo - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true \ No newline at end of file diff --git a/gitops/components/argo/cwfts/configmap-nodejs.yaml b/gitops/components/argo/cwfts/configmap-nodejs.yaml deleted file mode 100644 index d89753c66..000000000 --- a/gitops/components/argo/cwfts/configmap-nodejs.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: nodejs-scripts - namespace: argo -data: - update-chart-version.js: | - const yaml = require('js-yaml'); - const fs = require('fs'); - - try { - const chartFilepath = process.argv.slice(2)[0] - const chartVersion = process.argv.slice(2)[1] - let doc = yaml.load(fs.readFileSync(chartFilepath, 'utf8')); - doc.version = chartVersion - console.log("set version to", doc.version); - - fs.writeFile(chartFilepath, yaml.dump(doc), err => { - if (err) { - console.error(err) - return - } - console.log(yaml.dump(doc)) - }) - } catch (e) { - console.log(e); - } - get-chart-version.js: | - const yaml = require('js-yaml'); - const fs = require('fs'); - - try { - const chartFilepath = process.argv.slice(2)[0] - const doc = yaml.load(fs.readFileSync(chartFilepath, 'utf8')); - console.log(doc.version); - } catch (e) { - console.log(e); - } diff --git a/gitops/components/argo/cwfts/cwft-ci.yaml b/gitops/components/argo/cwfts/cwft-ci.yaml deleted file mode 100644 index d46af7e0a..000000000 --- a/gitops/components/argo/cwfts/cwft-ci.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: ClusterWorkflowTemplate -metadata: - name: cwft-ci -spec: - entrypoint: ci-setup-src - templates: - - name: ci-setup-src - outputs: - artifacts: - - name: repo-source - path: /src - container: - image: golang:latest - command: ["/bin/sh", "-c"] - args: - - mkdir /src \ No newline at end of file diff --git a/gitops/components/argo/cwfts/cwft-docker.yaml b/gitops/components/argo/cwfts/cwft-docker.yaml deleted file mode 100644 index c6e10a415..000000000 --- a/gitops/components/argo/cwfts/cwft-docker.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: ClusterWorkflowTemplate -metadata: - name: cwft-docker -spec: - entrypoint: docker-build - templates: - - name: docker-build - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - - name: appName - - name: awsRegion - value: "{{workflow.parameters.awsRegion}}" - default: - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - - name: ciCommitSha - - name: containerRegistryUrl - value: "{{workflow.parameters.containerRegistryUrl}}" - default: .dkr.ecr..amazonaws.com - container: - image: "{{inputs.parameters.buildImage}}" - command: [bash, -c] - workingDir: "{{inputs.parameters.appDir}}" - # todo need to add auth to trivy ( or put it behind a vpn.. ) - args: - - until docker ps; do sleep 3; done; - docker build -t "{{inputs.parameters.containerRegistryUrl}}/{{inputs.parameters.appName}}:{{inputs.parameters.ciCommitSha}}" .; - aws ecr get-login-password --region {{inputs.parameters.awsRegion}} | docker login --username AWS --password-stdin {{inputs.parameters.containerRegistryUrl}}; - docker push "{{inputs.parameters.containerRegistryUrl}}/{{inputs.parameters.appName}}:{{inputs.parameters.ciCommitSha}}"; - envFrom: - - secretRef: - name: ci-secrets - env: - - name: DOCKER_HOST - value: "tcp://localhost:2375" - sidecars: - - name: dind - image: docker:19.03.13-dind - env: - - name: DOCKER_TLS_CERTDIR - value: "" - securityContext: - privileged: true - mirrorVolumeMounts: true diff --git a/gitops/components/argo/cwfts/cwft-git.yaml b/gitops/components/argo/cwfts/cwft-git.yaml deleted file mode 100644 index dfc0a49ff..000000000 --- a/gitops/components/argo/cwfts/cwft-git.yaml +++ /dev/null @@ -1,103 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: ClusterWorkflowTemplate -metadata: - name: cwft-git -spec: - templates: - - name: git-checkout-with-gitops - inputs: - parameters: - - name: appDir - - name: branch - value: "{{workflow.parameters.branch}}" - default: main - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - - name: gitopsRepoUrl - value: "{{workflow.parameters.gitopsRepoUrl}}" - default: https://gitlab./kubefirst/gitops - - name: gitRepoUrl - artifacts: - - name: repo-source - path: "{{inputs.parameters.appDir}}" - git: - repo: "{{inputs.parameters.gitRepoUrl}}" - revision: "{{inputs.parameters.branch}}" - usernameSecret: - name: ci-secrets - key: USERNAME - passwordSecret: - name: ci-secrets - key: PERSONAL_ACCESS_TOKEN - - name: gitops-source - path: /src/gitops - git: - repo: "{{inputs.parameters.gitopsRepoUrl}}" - revision: "main" - usernameSecret: - name: ci-secrets - key: USERNAME - passwordSecret: - name: ci-secrets - key: PERSONAL_ACCESS_TOKEN - container: - image: golang:latest - command: ["/bin/sh", "-c"] - args: - - ls -la /src && - ls -la {{inputs.parameters.appDir}} - outputs: - artifacts: - - name: repo-source - path: /src - - - name: git-commit - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - - name: commitMessage - - name: repoPath - value: "{{workflow.parameters.repoPath}}" - default: gitlab./kubefirst/gitops - - name: gitUserEmail - value: "{{workflow.parameters.gitUserEmail}}" - default: - - name: gitUsername - value: "{{workflow.parameters.gitUsername}}" - default: gitlab-bot - container: - workingDir: "{{inputs.parameters.appDir}}" - image: golang:latest - command: ["/bin/sh", "-c"] - # todo is this remote set-url good enough? - args: - - git remote set-url origin https://$USERNAME:$PERSONAL_ACCESS_TOKEN@{{inputs.parameters.repoPath}} 2>/dev/null; - git remote -v; - git config --global user.email '{{inputs.parameters.gitUserEmail}}'; - git config --global user.name '{{inputs.parameters.gitUsername}}'; - git status; - git pull; - git add .; - git commit -m "{{inputs.parameters.commitMessage}}"; - git push - envFrom: - - secretRef: - name: ci-secrets - - - name: git-short-sha - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - script: - image: golang:latest - workingDir: "{{inputs.parameters.appDir}}" - command: [bash] - source: | - git log -1 --pretty=format:%h \ No newline at end of file diff --git a/gitops/components/argo/cwfts/cwft-helm.yaml b/gitops/components/argo/cwfts/cwft-helm.yaml deleted file mode 100644 index d56691559..000000000 --- a/gitops/components/argo/cwfts/cwft-helm.yaml +++ /dev/null @@ -1,190 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: ClusterWorkflowTemplate -metadata: - name: cwft-helm -spec: - entrypoint: helm-publish-chart - templates: - - name: helm-publish-chart - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - - name: chartDir - - name: chartRepoName - value: "{{workflow.parameters.chartRepoName}}" - default: kubefirst - - name: chartRepoUrl - value: "{{workflow.parameters.chartRepoUrl}}" - default: https://chartmuseum. - container: - image: "{{inputs.parameters.buildImage}}" - command: [bash, -c] - workingDir: "{{inputs.parameters.appDir}}" - args: - - helm repo add {{inputs.parameters.chartRepoName}} {{inputs.parameters.chartRepoUrl}} --username $BASIC_AUTH_USER --password $BASIC_AUTH_PASS; - helm push {{inputs.parameters.chartDir}} {{inputs.parameters.chartRepoName}}; - envFrom: - - secretRef: - name: ci-secrets - - - name: helm-set-chart-version - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: chartFilepath - - name: chartVersion - - name: appName - container: - image: node:14.16-buster - command: [sh, -c] - workingDir: "/src/{{inputs.parameters.appName}}" - args: - - npm i -g js-yaml; - node /scripts/update-chart-version.js "{{inputs.parameters.chartFilepath}}" "{{inputs.parameters.chartVersion}}"; - env: - - name: NODE_PATH - value: /usr/local/lib/node_modules - volumeMounts: - - name: scripts - mountPath: /scripts - outputs: - artifacts: - - name: repo-source - path: /src - volumes: - - name: scripts - configMap: - name: nodejs-scripts - defaultMode: 0777 - - - name: helm-set-chart-versions - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - - name: chartDir - - name: chartVersion - - name: ciCommitSha - script: - image: "{{inputs.parameters.buildImage}}" - command: [bash] - workingDir: "{{inputs.parameters.appDir}}" - source: | - set -e - NEW_CHART_VERSION={{inputs.parameters.chartVersion}} - echo "setting ./{{inputs.parameters.chartDir}}/Chart.yaml to version: ${NEW_CHART_VERSION}" - sed -i "s/version:.*/version: ${NEW_CHART_VERSION}/g" {{inputs.parameters.appDir}}/{{inputs.parameters.chartDir}}/Chart.yaml - echo "setting {{inputs.parameters.chartDir}}/Chart.yaml to appVersion: {{inputs.parameters.ciCommitSha}}" - sed -i "s/appVersion:.*/appVersion: {{inputs.parameters.ciCommitSha}}/g" {{inputs.parameters.appDir}}/{{inputs.parameters.chartDir}}/Chart.yaml - echo "adjusted chart:" - cat {{inputs.parameters.appDir}}/{{inputs.parameters.chartDir}}/Chart.yaml - outputs: - artifacts: - - name: repo-source - path: /src - - - name: helm-get-chart-release-from-version - inputs: - parameters: - - name: buildImage - - name: chartVersion - script: - image: "{{inputs.parameters.buildImage}}" - command: [python3] - source: | - print("{{inputs.parameters.chartVersion}}".split("-rc")[0]) - - - - name: helm-get-chart-version - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - - name: chartDir - script: - image: "{{inputs.parameters.buildImage}}" - workingDir: "{{inputs.parameters.appDir}}" - command: [python3] - source: | - import yaml, semver - with open('./{{inputs.parameters.chartDir}}/Chart.yaml') as f: - chart_yaml = yaml.load(f, Loader=yaml.FullLoader) - print(chart_yaml['version']) - - - name: helm-set-environment-version - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - - name: fullChartPath - - name: chartVersion - - name: environment - script: - image: "{{inputs.parameters.buildImage}}" - command: [bash] - workingDir: "/src/gitops" - source: | - set -e - NEW_CHART_VERSION={{inputs.parameters.chartVersion}} - echo "setting wrapper Chart.yaml to version: ${NEW_CHART_VERSION}" - sed -i "s/ version:.*/ version: ${NEW_CHART_VERSION}/g" "{{inputs.parameters.fullChartPath}}" - echo "updated {{inputs.parameters.environment}} wrapper chart version to ${NEW_CHART_VERSION}" - outputs: - artifacts: - - name: repo-source - path: /src - - - name: helm-increment-chart-patch - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - - name: chartDir - - name: chartVersion - # version bumps the patch verison of the helm chart version - script: - image: "{{inputs.parameters.buildImage}}" - command: [python3] - workingDir: "{{inputs.parameters.appDir}}" - source: | - import yaml, semver - with open('./{{inputs.parameters.chartDir}}/Chart.yaml') as f: - chart_yaml = yaml.load(f, Loader=yaml.FullLoader) - chart_version = semver.parse('{{inputs.parameters.chartVersion}}') - next_chart_version = '{}.{}.{}'.format(chart_version['major'],chart_version['minor'],chart_version['patch']+1) - chart_yaml['version'] = next_chart_version - with open('./{{inputs.parameters.chartDir}}/Chart.yaml', 'w') as f: - yaml.dump(chart_yaml, f) - print('prepared next release in {{inputs.parameters.chartDir}} with bumped chart version after releasing {}'.format(next_chart_version)) - outputs: - artifacts: - - name: repo-source - path: /src - diff --git a/gitops/components/argo/cwfts/cwft-npm.yaml b/gitops/components/argo/cwfts/cwft-npm.yaml deleted file mode 100644 index 3a3053da4..000000000 --- a/gitops/components/argo/cwfts/cwft-npm.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: ClusterWorkflowTemplate -metadata: - name: cwft-npm - # todo, follow up on wheter we can use global params on a cwft to reduce duplication in templates -spec: - entrypoint: npm-install - templates: - - name: npm-install - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - container: - image: "{{inputs.parameters.buildImage}}" - command: [bash, -c] - workingDir: "{{inputs.parameters.appDir}}" - args: - - npm i - outputs: - artifacts: - - name: repo-source - path: /src - - - name: npm-run - inputs: - artifacts: - - name: repo-source - path: /src - parameters: - - name: appDir - - name: buildImage - value: "{{workflow.parameters.buildImage}}" - default: kubefirst/chubbo:0.1 - - name: npmRunScript - container: - image: "{{inputs.parameters.buildImage}}" - command: [bash, -c] - workingDir: "{{inputs.parameters.appDir}}" - args: - - npm run {{inputs.parameters.npmRunScript}} diff --git a/gitops/components/argo/externalsecrets.yaml b/gitops/components/argo/externalsecrets.yaml deleted file mode 100644 index d8586a088..000000000 --- a/gitops/components/argo/externalsecrets.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: "external-secrets.io/v1alpha1" -kind: ExternalSecret -metadata: - name: ci-secrets -spec: - target: - name: ci-secrets - secretStoreRef: - kind: ClusterSecretStore - name: vault-secrets-backend - refreshInterval: 10s - data: - # username used for accessing git - this is the name of the personal access token, default is kubefirst - - remoteRef: - key: ci-secrets - property: USERNAME - secretKey: USERNAME - # personal access token associated with username above - - remoteRef: - key: ci-secrets - property: PERSONAL_ACCESS_TOKEN - secretKey: PERSONAL_ACCESS_TOKEN - # access key id to connect to aws - - remoteRef: - key: ci-secrets - property: AWS_ACCESS_KEY_ID - secretKey: AWS_ACCESS_KEY_ID - # access key to connect to aws - - remoteRef: - key: ci-secrets - property: AWS_SECRET_ACCESS_KEY - secretKey: AWS_SECRET_ACCESS_KEY - # chartmuseum admin username - - remoteRef: - key: ci-secrets - property: BASIC_AUTH_USER - secretKey: BASIC_AUTH_USER - # chartmuseum admin password - - remoteRef: - key: ci-secrets - property: BASIC_AUTH_PASS - secretKey: BASIC_AUTH_PASS ---- -apiVersion: "external-secrets.io/v1alpha1" -kind: ExternalSecret -metadata: - name: argo-secrets -spec: - target: - name: argo-secrets - secretStoreRef: - kind: ClusterSecretStore - name: vault-secrets-backend - refreshInterval: 10s - data: - # client-id used for oidc auth with keycloak - - remoteRef: - key: admin/oidc/argo - property: application_id - secretKey: client-id - # client-id used for oidc auth with keycloak - - remoteRef: - key: admin/oidc/argo - property: secret - secretKey: client-secret diff --git a/gitops/components/argo/ingress.yaml b/gitops/components/argo/ingress.yaml deleted file mode 100644 index df33178e5..000000000 --- a/gitops/components/argo/ingress.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: argo-server - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: "letsencrypt-prod" - nginx.ingress.kubernetes.io/backend-protocol: https - nginx.ingress.kubernetes.io/force-ssl-redirect: "true" -spec: - rules: - - host: argo. - http: - paths: - - backend: - serviceName: argo-server - servicePort: 2746 - tls: - - hosts: - - argo. - secretName: argo-secret diff --git a/gitops/components/argocd/configmap.yaml b/gitops/components/argocd/configmap.yaml deleted file mode 100644 index c319204c4..000000000 --- a/gitops/components/argocd/configmap.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - app.kubernetes.io/name: argocd-rbac-cm - app.kubernetes.io/part-of: argocd - name: argocd-rbac-cm -data: - policy.csv: | - g, admins, role:admin - policy.default: role:readonly ---- -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - app.kubernetes.io/name: argocd-cm - app.kubernetes.io/part-of: argocd - name: argocd-cm -data: - resource.customizations: | - admissionregistration.k8s.io/MutatingWebhookConfiguration: - ignoreDifferences: | - jsonPointers: - - /webhooks -# NOTE: automation appends content to the end of this file -- do not adjust in nebulous repo diff --git a/gitops/components/argocd/ingress.yaml b/gitops/components/argocd/ingress.yaml deleted file mode 100644 index 035f2f3b9..000000000 --- a/gitops/components/argocd/ingress.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: "letsencrypt-prod" - nginx.ingress.kubernetes.io/ssl-passthrough: "true" - name: argocd-server-ingress - namespace: argocd - labels: - run: argocd-server -spec: - rules: - - host: argocd. - http: - paths: - - backend: - serviceName: argocd-server - servicePort: https - path: / - tls: - - hosts: - - argocd. - secretName: argocd-secret \ No newline at end of file diff --git a/gitops/components/atlantis/application.yaml b/gitops/components/atlantis/application.yaml deleted file mode 100644 index fa3bc0768..000000000 --- a/gitops/components/atlantis/application.yaml +++ /dev/null @@ -1,69 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: atlantis - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "15" -spec: - project: default - source: - repoURL: 'https://runatlantis.github.io/helm-charts' - chart: atlantis - targetRevision: 3.15.0 - helm: - parameters: - - name: ingress.host - value: atlantis. - - name: orgWhitelist - value: gitlab./kubefirst/gitops - values: |- - resources: - limits: - cpu: 400m - memory: 1Gi - requests: - cpu: 400m - memory: 1Gi - extraVolumes: - - name: kubeconfig - configMap: - name: kubeconfig - items: - - key: config - path: config - extraVolumeMounts: - - name: kubeconfig - mountPath: /.kube - ingress: - enabled: true - annotations: - # kubernetes.io/tls-acme: "true" - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: "letsencrypt-prod" - path: / - host: atlantis. - tls: - - secretName: atlantis-tls - hosts: - - atlantis. - loadEnvFromSecrets: - - atlantis-secrets - repoConfig: | - --- - repos: - - id: gitlab./kubefirst/gitops - workflow: default - allowed_overrides: [apply_requirements] - apply_requirements: [approved, mergeable] - destination: - server: "https://kubernetes.default.svc" - namespace: atlantis - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true \ No newline at end of file diff --git a/gitops/components/atlantis/configmap.yaml b/gitops/components/atlantis/configmap.yaml deleted file mode 100644 index 3b6d53c4f..000000000 --- a/gitops/components/atlantis/configmap.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: kubeconfig -data: - config: | - apiVersion: v1 - preferences: {} - kind: Config - - clusters: - - cluster: - server: https://5A4DAC055CFB9B7FC78E2B38B9E1777B.gr7.us-east-1.eks.amazonaws.com - certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeE1EZ3hOekF4TkRReU4xb1hEVE14TURneE5UQXhORFF5TjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTThzCmRHZUZuQmF2aTZRYkYyR2Z3UHlYUmhHUU5SQUMrTnpnVU1TUTgrejVkaHBwZU42YUM1S3dDa2Z3K2M4WUExSVQKS3JNRklpWHZqR2FKRFBCUFJocFN0Wm9SMVJPV3pkcDhsQ24wYncweWtaaUJ0WU1rbGd1YnJ2THZaa0xPZEJ2Mgphc2xTNWFNMGYxWWt1OEhuOHZUdmlmdDc0VUlSMWQ3V3JDV25HUXhkQjdtSjcySVZsRU1uRjcvdFZDOVhVRWJSCnFWUGtLZC93bzlFSUFEV3VjczdwVDBBeDJhWi9JbzhmTHJMUDVrV1ZxeWJLY2FMZFlPQUo2ZUtzZjFmTW8xNjYKRnVSdC93WXJIRGVyTWI0ZVdraC80RkFNd21KdFVMTjVMVFpOMDNzZkM4akdNcytCQ3UrMEo2QnVEeGZKb2FBTgp3dU1lYld1SThnTkIwOXluU2tzQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMZ0pqMVNOd3AxT1BUM2Y3eFVESzk4UWFIQloKSzhLc3dXTE5GTEdzckZYckFtdUZvQjFuandLbW5jTjlGVXY0cC9tRFJTS0xzaEt0cEt4dFBCaUlKQWxNcm9rMQpMSWU0Mmt2dWNNczBDRnVUclplWUVIRFhVWWFnaENHbHdkZE8ra3I5a2NCL2ZUSUpZcHJKdzVTdHVqeWQweGFhCjZ1bVlNVDB0ZDNVbXJBTlhQY1ExUTF6TVpCMGloajhCSmpJRDRValJMLy9pdzRSWUl2UEF6OEtXeSt6RDRONSsKUEZLdDl4NkhyV282dFg2aEg3Yms0c2ZsRkhlM3BzbklFUXVkZWpBNnBzN1d4b3ErWjJJM1c5R25PMTZKSVRmVAp1OURMZjRGQlNSSk9kQ1NveHlFVjZnS2hXMHBZWHpWVjUrb0ZiUjNjWCtpbDRHblBSSWxhY2VOcFdNaz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= - name: eks_kubefirst - - contexts: - - context: - cluster: eks_kubefirst - user: eks_kubefirst - name: eks_kubefirst - - current-context: eks_kubefirst - - users: - - name: eks_kubefirst - user: - exec: - apiVersion: client.authentication.k8s.io/v1alpha1 - command: aws-iam-authenticator - args: - - "token" - - "-i" - - "kubefirst" diff --git a/gitops/components/atlantis/externalsecret.yaml b/gitops/components/atlantis/externalsecret.yaml deleted file mode 100644 index 3f8b1e1eb..000000000 --- a/gitops/components/atlantis/externalsecret.yaml +++ /dev/null @@ -1,204 +0,0 @@ -apiVersion: "external-secrets.io/v1alpha1" -kind: ExternalSecret -metadata: - name: atlantis-secrets -spec: - target: - name: atlantis-secrets - secretStoreRef: - kind: ClusterSecretStore - name: vault-secrets-backend - refreshInterval: 10s - data: - - remoteRef: - key: atlantis - property: ARGOCD_AUTH_PASSWORD - secretKey: ARGOCD_AUTH_PASSWORD - - remoteRef: - key: atlantis - property: ARGOCD_AUTH_USERNAME - secretKey: ARGOCD_AUTH_USERNAME - - remoteRef: - key: atlantis - property: ARGOCD_INSECURE - secretKey: ARGOCD_INSECURE - - remoteRef: - key: atlantis - property: ARGOCD_SERVER - secretKey: ARGOCD_SERVER - - remoteRef: - key: atlantis - property: ARGO_SERVER_URL - secretKey: ARGO_SERVER_URL - - remoteRef: - key: atlantis - property: ATLANTIS_GITLAB_TOKEN - secretKey: ATLANTIS_GITLAB_TOKEN - - remoteRef: - key: atlantis - property: ATLANTIS_GITLAB_WEBHOOK_SECRET - secretKey: ATLANTIS_GITLAB_WEBHOOK_SECRET - - remoteRef: - key: atlantis - property: ATLANTIS_GITLAB_USER - secretKey: ATLANTIS_GITLAB_USER - - remoteRef: - key: atlantis - property: ATLANTIS_GITLAB_HOSTNAME - secretKey: ATLANTIS_GITLAB_HOSTNAME - - remoteRef: - key: atlantis - property: AWS_ACCESS_KEY_ID - secretKey: AWS_ACCESS_KEY_ID - - remoteRef: - key: atlantis - property: AWS_DEFAULT_REGION - secretKey: AWS_DEFAULT_REGION - - remoteRef: - key: atlantis - property: AWS_ROLE_TO_ASSUME - secretKey: AWS_ROLE_TO_ASSUME - - remoteRef: - key: atlantis - property: AWS_SECRET_ACCESS_KEY - secretKey: AWS_SECRET_ACCESS_KEY - - remoteRef: - key: atlantis - property: AWS_SESSION_NAME - secretKey: AWS_SESSION_NAME - - remoteRef: - key: atlantis - property: GITLAB_BASE_URL - secretKey: GITLAB_BASE_URL - - remoteRef: - key: atlantis - property: GITLAB_TOKEN - secretKey: GITLAB_TOKEN - - remoteRef: - key: atlantis - property: KEYCLOAK_CLIENT_ID - secretKey: KEYCLOAK_CLIENT_ID - - remoteRef: - key: atlantis - property: KEYCLOAK_PASSWORD - secretKey: KEYCLOAK_PASSWORD - - remoteRef: - key: atlantis - property: KEYCLOAK_REALM - secretKey: KEYCLOAK_REALM - - remoteRef: - key: atlantis - property: KEYCLOAK_URL - secretKey: KEYCLOAK_URL - - remoteRef: - key: atlantis - property: KEYCLOAK_USER - secretKey: KEYCLOAK_USER - - remoteRef: - key: atlantis - property: KUBECONFIG - secretKey: KUBECONFIG - - remoteRef: - key: atlantis - property: TF_VAR_argo_redirect_uris - secretKey: TF_VAR_argo_redirect_uris - - remoteRef: - key: atlantis - property: TF_VAR_argocd_auth_password - secretKey: TF_VAR_argocd_auth_password - - remoteRef: - key: atlantis - property: TF_VAR_argocd_redirect_uris - secretKey: TF_VAR_argocd_redirect_uris - - remoteRef: - key: atlantis - property: TF_VAR_atlantis_gitlab_token - secretKey: TF_VAR_atlantis_gitlab_token - - remoteRef: - key: atlantis - property: TF_VAR_atlantis_gitlab_webhook_secret - secretKey: TF_VAR_atlantis_gitlab_webhook_secret - - remoteRef: - key: atlantis - property: TF_VAR_aws_access_key_id - secretKey: TF_VAR_aws_access_key_id - - remoteRef: - key: atlantis - property: TF_VAR_aws_account_id - secretKey: TF_VAR_aws_account_id - - remoteRef: - key: atlantis - property: TF_VAR_aws_secret_access_key - secretKey: TF_VAR_aws_secret_access_key - - remoteRef: - key: atlantis - property: TF_VAR_aws_region - secretKey: TF_VAR_aws_region - - remoteRef: - key: atlantis - property: TF_VAR_email_address - secretKey: TF_VAR_email_address - - remoteRef: - key: atlantis - property: TF_VAR_email_domain - secretKey: TF_VAR_email_domain - - remoteRef: - key: atlantis - property: TF_VAR_gitlab_bot_root_password - secretKey: TF_VAR_gitlab_bot_root_password - - remoteRef: - key: atlantis - property: TF_VAR_gitlab_redirect_uris - secretKey: TF_VAR_gitlab_redirect_uris - - remoteRef: - key: atlantis - property: TF_VAR_gitlab_runner_token - secretKey: TF_VAR_gitlab_runner_token - - remoteRef: - key: atlantis - property: TF_VAR_gitlab_token - secretKey: TF_VAR_gitlab_token - - remoteRef: - key: atlantis - property: TF_VAR_gitlab_url - secretKey: TF_VAR_gitlab_url - - remoteRef: - key: atlantis - property: TF_VAR_hosted_zone_id - secretKey: TF_VAR_hosted_zone_id - - remoteRef: - key: atlantis - property: TF_VAR_hosted_zone_name - secretKey: TF_VAR_hosted_zone_name - - remoteRef: - key: atlantis - property: TF_VAR_iam_user_arn - secretKey: TF_VAR_iam_user_arn - - remoteRef: - key: atlantis - property: TF_VAR_keycloak_admin_password - secretKey: TF_VAR_keycloak_admin_password - - remoteRef: - key: atlantis - property: TF_VAR_keycloak_password - secretKey: TF_VAR_keycloak_password - - remoteRef: - key: atlantis - property: TF_VAR_vault_addr - secretKey: TF_VAR_vault_addr - - remoteRef: - key: atlantis - property: TF_VAR_vault_redirect_uris - secretKey: TF_VAR_vault_redirect_uris - - remoteRef: - key: atlantis - property: TF_VAR_vault_token - secretKey: TF_VAR_vault_token - - remoteRef: - key: atlantis - property: VAULT_TOKEN - secretKey: VAULT_TOKEN - - remoteRef: - key: atlantis - property: VAULT_ADDR - secretKey: VAULT_ADDR diff --git a/gitops/components/cert-issuers/letsencrypt.yaml b/gitops/components/cert-issuers/letsencrypt.yaml deleted file mode 100644 index 18ff41f01..000000000 --- a/gitops/components/cert-issuers/letsencrypt.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: ClusterIssuer -metadata: - name: letsencrypt-staging -spec: - acme: - server: https://acme-staging-v02.api.letsencrypt.org/directory - email: - privateKeySecretRef: - name: letsencrypt-staging - solvers: - - http01: - ingress: - class: nginx ---- -apiVersion: cert-manager.io/v1 -kind: ClusterIssuer -metadata: - name: letsencrypt-prod -spec: - acme: - server: https://acme-v02.api.letsencrypt.org/directory - email: - privateKeySecretRef: - name: letsencrypt-prod - solvers: - - http01: - ingress: - class: nginx diff --git a/gitops/components/chartmuseum/application.yaml b/gitops/components/chartmuseum/application.yaml deleted file mode 100644 index 350f261c1..000000000 --- a/gitops/components/chartmuseum/application.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: chartmuseum - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "2" -spec: - project: default - source: - repoURL: 'https://chartmuseum.github.io/charts' - targetRevision: 3.4.0 - helm: - values: |- - env: - open: - AUTH_ANONYMOUS_GET: true - STORAGE: amazon - STORAGE_AMAZON_BUCKET: - STORAGE_AMAZON_PREFIX: kubefirst-charts - STORAGE_AMAZON_REGION: - DISABLE_API: false - existingSecret: chartmuseum-secrets - chart: chartmuseum - destination: - server: 'https://kubernetes.default.svc' - namespace: chartmuseum - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true \ No newline at end of file diff --git a/gitops/components/chartmuseum/externalsecret.yaml b/gitops/components/chartmuseum/externalsecret.yaml deleted file mode 100644 index 4615d60be..000000000 --- a/gitops/components/chartmuseum/externalsecret.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: "external-secrets.io/v1alpha1" -kind: ExternalSecret -metadata: - name: chartmuseum-secrets -spec: - target: - name: chartmuseum-secrets - secretStoreRef: - kind: ClusterSecretStore - name: vault-secrets-backend - refreshInterval: 10s - data: - - remoteRef: - key: chartmuseum - property: AWS_ACCESS_KEY_ID - secretKey: AWS_ACCESS_KEY_ID - - remoteRef: - key: chartmuseum - property: AWS_SECRET_ACCESS_KEY - secretKey: AWS_SECRET_ACCESS_KEY - - remoteRef: - key: chartmuseum - property: BASIC_AUTH_USER - secretKey: BASIC_AUTH_USER - - remoteRef: - key: chartmuseum - property: BASIC_AUTH_PASS - secretKey: BASIC_AUTH_PASS diff --git a/gitops/components/chartmuseum/ingress.yaml b/gitops/components/chartmuseum/ingress.yaml deleted file mode 100644 index eb529922c..000000000 --- a/gitops/components/chartmuseum/ingress.yaml +++ /dev/null @@ -1,30 +0,0 @@ - -# todo try the native helm chart ingress now that we're 1.20+ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - annotations: - argocd.argoproj.io/sync-wave: "3" - cert-manager.io/cluster-issuer: letsencrypt-prod - kubernetes.io/ingress.class: nginx - labels: - app.kubernetes.io/instance: chartmuseum - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/name: chartmuseum - app.kubernetes.io/version: 0.13.1 - helm.sh/chart: chartmuseum-3.1.0 - name: chartmuseum - namespace: chartmuseum -spec: - rules: - - host: chartmuseum. - http: - paths: - - backend: - serviceName: chartmuseum - servicePort: 8080 - path: / - tls: - - hosts: - - chartmuseum. - secretName: chartmuseum-tls \ No newline at end of file diff --git a/gitops/components/development/metaphor.yaml b/gitops/components/development/metaphor.yaml deleted file mode 100644 index f2397df26..000000000 --- a/gitops/components/development/metaphor.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: metaphor-development - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops - path: components/development/metaphor - targetRevision: HEAD - helm: - valueFiles: - - values.yaml - destination: - server: https://kubernetes.default.svc - namespace: development - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/components/development/metaphor/Chart.yaml b/gitops/components/development/metaphor/Chart.yaml deleted file mode 100644 index ebf65d342..000000000 --- a/gitops/components/development/metaphor/Chart.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v2 -dependencies: -- name: metaphor - repository: https://chartmuseum. - version: 0.1.0 -description: metaphor instance -name: metaphor-development -type: application -version: 1.0.0 diff --git a/gitops/components/development/metaphor/values.yaml b/gitops/components/development/metaphor/values.yaml deleted file mode 100644 index f370922a0..000000000 --- a/gitops/components/development/metaphor/values.yaml +++ /dev/null @@ -1,16 +0,0 @@ -metaphor: - ingress: - enabled: true - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: "letsencrypt-prod" - hosts: - - host: metaphor-development. - paths: - - / - tls: - - secretName: metaphor-tls - hosts: - - metaphor-development. - vaultMountPoint: kubefirst - vaultSecretPath: development/metaphor diff --git a/gitops/components/external-secrets-operator/auth-delegator-crb.yaml b/gitops/components/external-secrets-operator/auth-delegator-crb.yaml deleted file mode 100644 index d5f28aeae..000000000 --- a/gitops/components/external-secrets-operator/auth-delegator-crb.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: eso-kubernetes-external-secrets-auth -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: 'system:auth-delegator' -subjects: - - kind: ServiceAccount - name: external-secrets - namespace: external-secrets-operator diff --git a/gitops/components/external-secrets-operator/external-secrets-operator.yaml b/gitops/components/external-secrets-operator/external-secrets-operator.yaml deleted file mode 100644 index 17aaa0833..000000000 --- a/gitops/components/external-secrets-operator/external-secrets-operator.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: external-secrets-operator - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "5" -spec: - project: default - source: - repoURL: 'https://charts.external-secrets.io' - targetRevision: 0.5.6 - helm: - values: |- - serviceAccount: - name: external-secrets - chart: external-secrets - destination: - server: https://kubernetes.default.svc - namespace: external-secrets-operator - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true - - RespectIgnoreDifferences=true - ignoreDifferences: - - group: apiextensions.k8s.io - kind: CustomResourceDefinition - jqPathExpressions: - - .spec.conversion.webhook.clientConfig.caBundle - - .spec.conversion.webhook.clientConfig.service.name - - .spec.conversion.webhook.clientConfig.service.namespace - - group: admissionregistration.k8s.io - kind: ValidatingWebhookConfiguration - jqPathExpressions: - - .webhooks[]?.clientConfig.caBundle diff --git a/gitops/components/external-secrets-store/vault-cluster-secret-store-secret.yaml b/gitops/components/external-secrets-store/vault-cluster-secret-store-secret.yaml deleted file mode 100644 index 521b39128..000000000 --- a/gitops/components/external-secrets-store/vault-cluster-secret-store-secret.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: external-secrets.io/v1alpha1 -kind: ClusterSecretStore -metadata: - name: vault-secrets-backend -spec: - provider: - vault: - server: "http://vault.vault.svc:8200" - # Path is the mount path of the Vault KV backend endpoint - path: "secret" - version: "v2" - auth: - kubernetes: - # Path where the Kubernetes authentication backend is mounted in Vault - mountPath: "kubernetes/kubefirst" - # A required field containing the Vault Role to assume. - role: "external-secrets" - serviceAccountRef: - name: "external-secrets" - namespace: "external-secrets-operator" diff --git a/gitops/components/gitlab-runner/application.yaml b/gitops/components/gitlab-runner/application.yaml deleted file mode 100644 index 50ce52e15..000000000 --- a/gitops/components/gitlab-runner/application.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: gitlab-runner - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "2" -spec: - destination: - server: https://kubernetes.default.svc - namespace: gitlab-runner - project: default - source: - repoURL: 'https://charts.gitlab.io' - targetRevision: 0.36.0 - helm: - parameters: - - name: rbac.create - value: 'true' - values: |- - gitlabUrl: https://gitlab./ - replicas: 2 - concurrent: 10 - hostNetwork: true - unregisterRunners: true - rbac: - create: true - clusterWideAccess: true - runners: - secret: gitlab-runner - privileged: true - config: | - [[runners]] - [runners.kubernetes] - image = "ubuntu:20.04" - privileged = true - [[runners.kubernetes.volumes.empty_dir]] - name = "docker-certs" - mount_path = "/certs/client" - medium = "Memory" - chart: gitlab-runner - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true \ No newline at end of file diff --git a/gitops/components/gitlab-runner/externalsecret.yaml b/gitops/components/gitlab-runner/externalsecret.yaml deleted file mode 100644 index 93fcff351..000000000 --- a/gitops/components/gitlab-runner/externalsecret.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: "external-secrets.io/v1alpha1" -kind: ExternalSecret -metadata: - name: gitlab-runner -spec: - target: - name: gitlab-runner - secretStoreRef: - kind: ClusterSecretStore - name: vault-secrets-backend - refreshInterval: 10s - data: - - remoteRef: - key: gitlab-runner - property: RUNNER_TOKEN - secretKey: runner-token - - remoteRef: - key: gitlab-runner - property: RUNNER_REGISTRATION_TOKEN - secretKey: runner-registration-token diff --git a/gitops/components/production/metaphor.yaml b/gitops/components/production/metaphor.yaml deleted file mode 100644 index 94a751ab8..000000000 --- a/gitops/components/production/metaphor.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: metaphor-production - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops - path: components/production/metaphor - targetRevision: HEAD - helm: - valueFiles: - - values.yaml - destination: - server: https://kubernetes.default.svc - namespace: production - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/components/production/metaphor/Chart.yaml b/gitops/components/production/metaphor/Chart.yaml deleted file mode 100644 index ce89dde3e..000000000 --- a/gitops/components/production/metaphor/Chart.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v2 -dependencies: -- name: metaphor - repository: https://chartmuseum. - version: 0.1.0 -description: metaphor instance -name: metaphor-production -type: application -version: 1.0.0 diff --git a/gitops/components/production/metaphor/values.yaml b/gitops/components/production/metaphor/values.yaml deleted file mode 100644 index 564be5650..000000000 --- a/gitops/components/production/metaphor/values.yaml +++ /dev/null @@ -1,16 +0,0 @@ -metaphor: - ingress: - enabled: true - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: "letsencrypt-prod" - hosts: - - host: metaphor-production. - paths: - - / - tls: - - secretName: metaphor-tls - hosts: - - metaphor-production. - vaultMountPoint: kubefirst - vaultSecretPath: production/metaphor diff --git a/gitops/components/staging/metaphor.yaml b/gitops/components/staging/metaphor.yaml deleted file mode 100644 index ee9969988..000000000 --- a/gitops/components/staging/metaphor.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: metaphor-staging - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops - path: components/staging/metaphor - targetRevision: HEAD - helm: - valueFiles: - - values.yaml - destination: - server: https://kubernetes.default.svc - namespace: staging - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/components/staging/metaphor/Chart.yaml b/gitops/components/staging/metaphor/Chart.yaml deleted file mode 100644 index a035c4bec..000000000 --- a/gitops/components/staging/metaphor/Chart.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v2 -dependencies: -- name: metaphor - repository: https://chartmuseum. - version: 0.1.0 -description: metaphor instance -name: metaphor-staging -type: application -version: 1.0.0 diff --git a/gitops/components/staging/metaphor/values.yaml b/gitops/components/staging/metaphor/values.yaml deleted file mode 100644 index d66027554..000000000 --- a/gitops/components/staging/metaphor/values.yaml +++ /dev/null @@ -1,16 +0,0 @@ -metaphor: - ingress: - enabled: true - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: "letsencrypt-prod" - hosts: - - host: metaphor-staging. - paths: - - / - tls: - - secretName: metaphor-tls - hosts: - - metaphor-staging. - vaultMountPoint: kubefirst - vaultSecretPath: staging/metaphor diff --git a/gitops/logo.png b/gitops/logo.png deleted file mode 100644 index ac69ec8df22b4ec4d651ed5332e80e0950866705..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78955 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 KRVQKg-~RxacRxq~ diff --git a/gitops/registry/argo.yaml b/gitops/registry/argo.yaml deleted file mode 100644 index c1b4ce89f..000000000 --- a/gitops/registry/argo.yaml +++ /dev/null @@ -1,79 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: argo-components - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "7" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/argo - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: argo - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: k-add-argocd-permissions - annotations: - argocd.argoproj.io/sync-wave: "8" - argocd.argoproj.io/hook: PostSync - # argocd.argoproj.io/hook-delete-policy: HookSucceeded -spec: - template: - spec: - containers: - # todo adjust node permissions to be adequate for geting a kubeconfig in terraform - manually adding in console - - name: argocd-permissions - image: kubefirst/kubefirst-builder:spike # TODO: official versioned image - command: - - /bin/bash - - -c - - | - aws eks update-kubeconfig --name kubefirst --region - kubectl -n argocd delete rolebinding argocd-admin --ignore-not-found=true - kubectl -n argocd create rolebinding argocd-admin --clusterrole=admin --serviceaccount=argocd:default - # todo remove this envFrom and assume role - envFrom: - - secretRef: - name: aws-creds - restartPolicy: Never - backoffLimit: 2 ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: k-add-argo-permissions - annotations: - argocd.argoproj.io/sync-wave: "9" -spec: - template: - spec: - containers: - - name: argo-permissions - image: kubefirst/kubefirst-builder:spike - command: - - /bin/bash - - -c - - | - aws eks update-kubeconfig --name kubefirst --region - kubectl -n argo delete rolebinding argo-admin --ignore-not-found=true - kubectl -n argo create rolebinding argo-admin --clusterrole=admin --serviceaccount=argo:default - # todo remove this envFrom and assume role - envFrom: - - secretRef: - name: aws-creds - restartPolicy: Never - backoffLimit: 2 diff --git a/gitops/registry/argocd.yaml b/gitops/registry/argocd.yaml deleted file mode 100644 index 39fcc2b61..000000000 --- a/gitops/registry/argocd.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: argocd - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "0" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/argocd - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: argocd - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true \ No newline at end of file diff --git a/gitops/registry/atlantis.yaml b/gitops/registry/atlantis.yaml deleted file mode 100644 index c4bc96f47..000000000 --- a/gitops/registry/atlantis.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: atlantis-components - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "13" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/atlantis - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: atlantis - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/registry/cert-issuers.yaml b/gitops/registry/cert-issuers.yaml deleted file mode 100644 index 352535e97..000000000 --- a/gitops/registry/cert-issuers.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: cert-issuers - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "4" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/cert-issuers - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: cert-manager - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true \ No newline at end of file diff --git a/gitops/registry/cert-manager.yaml b/gitops/registry/cert-manager.yaml deleted file mode 100644 index 93eed699f..000000000 --- a/gitops/registry/cert-manager.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: cert-manager - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "3" -spec: - project: default - source: - repoURL: https://charts.jetstack.io/ - chart: cert-manager - targetRevision: 1.6.1 - helm: - values: |- - installCRDs: true - destination: - server: https://kubernetes.default.svc - namespace: cert-manager - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true \ No newline at end of file diff --git a/gitops/registry/chartmuseum.yaml b/gitops/registry/chartmuseum.yaml deleted file mode 100644 index ea9b23da7..000000000 --- a/gitops/registry/chartmuseum.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: chartmuseum-components - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "11" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/chartmuseum - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: chartmuseum - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/registry/development.yaml b/gitops/registry/development.yaml deleted file mode 100644 index 450aee314..000000000 --- a/gitops/registry/development.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: development - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops - path: components/development - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: development - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/registry/external-dns.yaml b/gitops/registry/external-dns.yaml deleted file mode 100644 index c17b8dffe..000000000 --- a/gitops/registry/external-dns.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: external-dns - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - destination: - namespace: external-dns - server: https://kubernetes.default.svc - project: default - source: - chart: external-dns - repoURL: https://charts.bitnami.com/bitnami - targetRevision: 6.5.4 - helm: - releaseName: external-dns - values: | - domainFilters: - - - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/registry/external-secrets-operator.yaml b/gitops/registry/external-secrets-operator.yaml deleted file mode 100644 index 5708c8438..000000000 --- a/gitops/registry/external-secrets-operator.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: external-secrets-operator-components - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "5" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/external-secrets-operator - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: external-secrets-operator - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true \ No newline at end of file diff --git a/gitops/registry/external-secrets-store.yaml b/gitops/registry/external-secrets-store.yaml deleted file mode 100644 index 359cd8004..000000000 --- a/gitops/registry/external-secrets-store.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: external-secrets-store - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "7" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/external-secrets-store - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: external-secrets-operator - syncPolicy: - automated: - prune: true - selfHeal: true diff --git a/gitops/registry/gitlab-runner.yaml b/gitops/registry/gitlab-runner.yaml deleted file mode 100644 index 56c147334..000000000 --- a/gitops/registry/gitlab-runner.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: gitlab-runner-components - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "12" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops.git - path: components/gitlab-runner - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: gitlab-runner - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/registry/ingress-nginx.yaml b/gitops/registry/ingress-nginx.yaml deleted file mode 100644 index 76cb258c0..000000000 --- a/gitops/registry/ingress-nginx.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: ingress-nginx - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "2" -spec: - destination: - server: https://kubernetes.default.svc - namespace: ingress-nginx - project: default - source: - repoURL: 'https://kubernetes.github.io/ingress-nginx' - targetRevision: 3.10.1 - helm: - values: |- - controller: - publishService: - enabled: true - service: - annotations: - service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" - service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60" - extraArgs: - enable-ssl-passthrough: true - chart: ingress-nginx - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/registry/production.yaml b/gitops/registry/production.yaml deleted file mode 100644 index 1a97d6fa3..000000000 --- a/gitops/registry/production.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: production - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops - path: components/production - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: production - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/registry/staging.yaml b/gitops/registry/staging.yaml deleted file mode 100644 index d846d4161..000000000 --- a/gitops/registry/staging.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: staging - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "1" -spec: - project: default - source: - repoURL: https://gitlab./kubefirst/gitops - path: components/staging - targetRevision: HEAD - destination: - server: https://kubernetes.default.svc - namespace: staging - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/gitops/registry/vault.yaml b/gitops/registry/vault.yaml deleted file mode 100644 index 251c97aa6..000000000 --- a/gitops/registry/vault.yaml +++ /dev/null @@ -1,118 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: vault - namespace: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io - annotations: - argocd.argoproj.io/sync-wave: "6" -spec: - destination: - server: https://kubernetes.default.svc - namespace: vault - project: default - source: - repoURL: 'https://helm.releases.hashicorp.com' - targetRevision: 0.18.0 - helm: - parameters: - - name: server.route.host - value: vault. - - name: 'server.ingress.hosts[0].host' - value: vault. - values: |- - server: - image: - repository: "vault" - tag: "1.9.2" - affinity: "" - ha: - enabled: true - replicas: 3 - - # config is a raw string of default configuration when using a Stateful - # deployment. Default is to use a Consul for its HA storage backend. - # This should be HCL. - - # Note: Configuration files are stored in ConfigMaps so sensitive data - # such as passwords should be either mounted through extraSecretEnvironmentVars - # or through a Kube secret. For more information see: - # https://www.vaultproject.io/docs/platform/k8s/helm/run#protecting-sensitive-vault-configurations - config: | - ui = true - - listener "tcp" { - tls_disable = 1 - address = "[::]:8200" - cluster_address = "[::]:8201" - } - - storage "dynamodb" { - ha_enabled = "true" - region = "" - table = "vault-dynamodb-backend" - } - - seal "awskms" { - region = "" - kms_key_id = "" - } - service_registration "kubernetes" {} - - ui: - enabled: true - serviceType: "ClusterIP" - serviceNodePort: null - externalPort: 8200 - ingress: - enabled: true - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: "letsencrypt-prod" - path: / - host: vault. - tls: - - secretName: vault-tls - hosts: - - vault. - injector: - enabled: true - chart: vault - syncPolicy: - automated: - prune: true - selfHeal: true - # todo blue - mutating webhooks needs to be ignored in argocd - syncOptions: - - CreateNamespace=true ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: unseal-vault - annotations: - argocd.argoproj.io/sync-wave: "6" - # argocd.argoproj.io/hook: PostSync # don't PostSync, vault doesn't finish sync while waiting for pods to be running which requires this job - # argocd.argoproj.io/hook-delete-policy: HookSucceeded -spec: - template: - spec: - containers: - # todo adjust node permissions to be adequate for geting a kubeconfig in terraform - manually adding in console - - name: unseal-vault - image: kubefirst/kubefirst-builder:spike # TODO: official versioned image - command: - - /bin/bash - - -c - - | - sleep 30 - aws eks update-kubeconfig --name kubefirst --region - kubectl -n vault exec vault-0 -- vault operator init -format=json > cluster-keys.json - kubectl -n vault create secret generic vault-unseal-keys --from-file=cluster-keys.json - # todo remove this envFrom and assume role - envFrom: - - secretRef: - name: aws-creds - restartPolicy: Never - backoffLimit: 2 \ No newline at end of file diff --git a/gitops/terraform/argocd/backend.tf b/gitops/terraform/argocd/backend.tf deleted file mode 100644 index 38b5bacf4..000000000 --- a/gitops/terraform/argocd/backend.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - backend "s3" { - bucket = "" - key = "terraform/argocd/tfstate.tf" - region = "" - encrypt = true - } -} diff --git a/gitops/terraform/argocd/internal-repos/provider.tf b/gitops/terraform/argocd/internal-repos/provider.tf deleted file mode 100644 index 5413cb7b1..000000000 --- a/gitops/terraform/argocd/internal-repos/provider.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_providers { - argocd = { - source = "oboukili/argocd" - version = "1.2.2" - } - } -} - -provider "argocd" {} diff --git a/gitops/terraform/argocd/internal-repos/repos.tf b/gitops/terraform/argocd/internal-repos/repos.tf deleted file mode 100644 index cd263461d..000000000 --- a/gitops/terraform/argocd/internal-repos/repos.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "argocd_repository" "kubefirst-charts" { - repo = "https://chartmuseum." - type = "helm" - name = "kubefirst-charts" - # username = data.vault_generic_secret.chartmuseum_secrets.data["basic-auth-user"] - # password = data.vault_generic_secret.chartmuseum_secrets.data["basic-auth-pass"] -} diff --git a/gitops/terraform/argocd/main.tf b/gitops/terraform/argocd/main.tf deleted file mode 100644 index 0f2a629b2..000000000 --- a/gitops/terraform/argocd/main.tf +++ /dev/null @@ -1,16 +0,0 @@ -variable "gitlab_token" { - type = string -} - -module "argocd_repos" { - source = "./repos" - gitlab_token = var.gitlab_token -} - -module "argocd_internal_repos" { - source = "./internal-repos" -} - -module "argocd_registry" { - source = "./registry" -} diff --git a/gitops/terraform/argocd/registry/provider.tf b/gitops/terraform/argocd/registry/provider.tf deleted file mode 100644 index 5413cb7b1..000000000 --- a/gitops/terraform/argocd/registry/provider.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_providers { - argocd = { - source = "oboukili/argocd" - version = "1.2.2" - } - } -} - -provider "argocd" {} diff --git a/gitops/terraform/argocd/registry/registry.tf b/gitops/terraform/argocd/registry/registry.tf deleted file mode 100644 index 6dd597e66..000000000 --- a/gitops/terraform/argocd/registry/registry.tf +++ /dev/null @@ -1,7 +0,0 @@ -module "registry" { - source = "../templates/argocd-app" - - app_name = "registry" - resource_path = "registry" - recurse_directory = true -} diff --git a/gitops/terraform/argocd/repos/provider.tf b/gitops/terraform/argocd/repos/provider.tf deleted file mode 100644 index 5413cb7b1..000000000 --- a/gitops/terraform/argocd/repos/provider.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_providers { - argocd = { - source = "oboukili/argocd" - version = "1.2.2" - } - } -} - -provider "argocd" {} diff --git a/gitops/terraform/argocd/repos/repos.tf b/gitops/terraform/argocd/repos/repos.tf deleted file mode 100644 index 52cc6a141..000000000 --- a/gitops/terraform/argocd/repos/repos.tf +++ /dev/null @@ -1,77 +0,0 @@ -variable "gitlab_token" { - type = string -} - -#* private git repos -resource "argocd_repository_credentials" "private" { - url = "https://gitlab." - username = "argocd" - password = var.gitlab_token -} - -resource "argocd_repository" "kubefirst_argocd" { - repo = "https://gitlab./kubefirst/gitops.git" - type = "git" - depends_on = [argocd_repository_credentials.private] -} - -#* public helm chart repos -resource "argocd_repository" "bitnami" { - repo = "https://charts.bitnami.com/bitnami" - type = "helm" - name = "bitnami" -} - -resource "argocd_repository" "chartmuseum" { - repo = "https://chartmuseum.github.io/charts" - type = "helm" - name = "chartmuseum" -} - -resource "argocd_repository" "datadog" { - repo = "https://helm.datadoghq.com" - type = "helm" - name = "datadog" -} - -resource "argocd_repository" "external_secrets" { - repo = "https://external-secrets.github.io/kubernetes-external-secrets" - type = "helm" - name = "external-secrets" -} - -resource "argocd_repository" "gitlab" { - repo = "https://charts.gitlab.io" - type = "helm" - name = "gitlab" -} - -resource "argocd_repository" "hashicorp" { - repo = "https://helm.releases.hashicorp.com" - type = "helm" - name = "hashicorp" -} - -resource "argocd_repository" "ingress_nginx" { - repo = "https://kubernetes.github.io/ingress-nginx" - type = "helm" - name = "ingress-nginx" -} - -resource "argocd_repository" "jet_stack" { - repo = "https://charts.jetstack.io" - type = "helm" - name = "jet-stack" -} - -resource "argocd_repository" "kube2iam" { - repo = "https://jtblin.github.io/kube2iam" - type = "helm" - name = "kube2iam" -} - -resource "argocd_repository" "runatlantis" { - repo = "https://runatlantis.github.io/helm-charts" - type = "helm" - name = "atlantis" -} diff --git a/gitops/terraform/argocd/templates/argocd-app/main.tf b/gitops/terraform/argocd/templates/argocd-app/main.tf deleted file mode 100644 index 0373408ed..000000000 --- a/gitops/terraform/argocd/templates/argocd-app/main.tf +++ /dev/null @@ -1,88 +0,0 @@ -# data "vault_generic_secret" "argocd_secrets" { -# path = "secret/k8s-mgmt/argocd/argocd-west-login" # TODO: change -# } - -terraform { - required_providers { - argocd = { - source = "oboukili/argocd" - version = "1.2.2" - } - } -} - -provider "argocd" {} - -variable "resource_path" { - type = string -} - -variable "destination_cluster" { - type = string - default = "https://kubernetes.default.svc" -} - -variable "repo_url" { - type = string - default = "https://gitlab./kubefirst/gitops.git" -} - -variable "destination_namespace" { - type = string - default = "argocd" -} - -variable "app_name" { - type = string -} - -variable "recurse_directory" { - type = bool - default = false -} - -resource "argocd_application" "app" { - metadata { - namespace = "argocd" - name = "app-${var.app_name}" - labels = { - test = "true" - } - } - - wait = false # todo true - - spec { - source { - repo_url = var.repo_url - path = var.resource_path - target_revision = "HEAD" - - directory { - recurse = var.recurse_directory - } - } - sync_policy { - automated = { - prune = true - self_heal = true - allow_empty = false - } - - sync_options = ["Validate=false"] - retry { - limit = "5" - backoff = { - duration = "30s" - max_duration = "2m" - factor = "2" - } - } - } - - destination { - server = var.destination_cluster - namespace = var.destination_namespace - } - } -} diff --git a/gitops/terraform/base/cypress/.DS_Store b/gitops/terraform/base/cypress/.DS_Store deleted file mode 100644 index 0be3ab5525a920d0e8d41feea5fed774aa1bbdac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOG-mQ5UkdK0XJD@IalxoLx?BH1p+Z-BT*6*+`U!K<ho0%Pu2!bnk81SHn91=I#uy`5bt{@1CS=@tSGu74tLd)lqQC0LG!e0O;n#^OL}Z|FEUlyZ zh46k}mSjb>a6cx-C@7_XvKvuyuw<Aw8j-X7;ZaB_8_t!EhDj z@^>N7h%8vgunwq){q8~wDWRC}`WdhH3tnX!P=p9Q;VsUHLu+AymieI5Pez^G&5Nw5x-ro!2-6$9a@vZcxn+NGVZoHQy5e%g6dUwNM)UBMomc+Rm#z88^lVjZ9 z4`V+WwUZ+DGhwd=7y4~C?_(U}qSJRUAtKW-yOoXcxY2A@?M7pBQnkmeRt@^*)?{KD z#`#Ov?>sD?K7aY@^_#cvK7JBj#Bc};Iiv6=e1>uap~FrZM@ig+1@|t4>H>)xfbNv? z`ING+u$Wy4YcxbMeJq4zF*Rb*yqH-ubG9cYA^8VP`7j|@kb`<{o9Cn6+-_CvW~=q5 z37I2gAq^tvu~%xj7}l{z(g8FBnt_wX06!mW6poIa{*V-tLP`GfrsYKy|N?pf7;aBkn ciV*bKTmd=?rxMWu(LVx$23=_e{wf1M0Qutz_5c6? diff --git a/gitops/terraform/base/cypress/cypress/fixtures/example.json b/gitops/terraform/base/cypress/cypress/fixtures/example.json deleted file mode 100644 index da18d9352..000000000 --- a/gitops/terraform/base/cypress/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/gitops/terraform/base/cypress/cypress/integration/gitlab-init.spec.js b/gitops/terraform/base/cypress/cypress/integration/gitlab-init.spec.js deleted file mode 100644 index 6a9c0d8ec..000000000 --- a/gitops/terraform/base/cypress/cypress/integration/gitlab-init.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -/// - -context('Window', () => { - before(() => { - cy.visit('/') - }) - - it('logs in with root user', () => { - cy.get('#user_login') - .type(Cypress.env('gitlab_bot_username_before')) - cy.get('#user_password') - .type(Cypress.env('gitlab_bot_password')) - cy.get('.gl-button').click() - }) - - it('sets up a personal access token', () => { - cy.visit('/-/profile/personal_access_tokens') - cy.get('#personal_access_token_name').type('kubefirst') - cy.get('#personal_access_token_scopes_api').check() - cy.get('#personal_access_token_scopes_write_repository').check() - cy.get('#personal_access_token_scopes_write_registry').check() - cy.get('#new_personal_access_token > .gl-mt-3 > .gl-button').click() - cy.get('#created-personal-access-token').then(elem => { - // elem is the underlying Javascript object targeted by the .get() command. - const token = Cypress.$(elem).val(); - cy.writeFile('../.gitlab-bot-access-token', token) - }) - }) - - it('gets the runner registration token', () => { - cy.visit('/admin/runners') - cy.get('[data-testid=eye-icon] > use').click() - cy.get('[data-testid=registration-token] > span').then(elem => { - // elem is the underlying Javascript object targeted by the .get() command. - const token = Cypress.$(elem).text(); - cy.writeFile('../.gitlab-runner-registration-token', token.trim()) - }) - }) - -}) diff --git a/gitops/terraform/base/cypress/cypress/plugins/index.js b/gitops/terraform/base/cypress/cypress/plugins/index.js deleted file mode 100644 index aa9918d21..000000000 --- a/gitops/terraform/base/cypress/cypress/plugins/index.js +++ /dev/null @@ -1,21 +0,0 @@ -/// -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -} diff --git a/gitops/terraform/base/cypress/cypress/support/commands.js b/gitops/terraform/base/cypress/cypress/support/commands.js deleted file mode 100644 index ca4d256f3..000000000 --- a/gitops/terraform/base/cypress/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/gitops/terraform/base/cypress/cypress/support/index.js b/gitops/terraform/base/cypress/cypress/support/index.js deleted file mode 100644 index f6d888f00..000000000 --- a/gitops/terraform/base/cypress/cypress/support/index.js +++ /dev/null @@ -1,29 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') -Cypress.Cookies.defaults({ - preserve: ['_gitlab_session', 'known_sign_in'] -}) - -Cypress.on('uncaught:exception', (err, runnable) => { - // returning false here prevents Cypress from - // failing the test - return false -}) diff --git a/gitops/terraform/base/cypress/package-lock.json b/gitops/terraform/base/cypress/package-lock.json deleted file mode 100644 index 91b3dd067..000000000 --- a/gitops/terraform/base/cypress/package-lock.json +++ /dev/null @@ -1,1578 +0,0 @@ -{ - "name": "gitlab-init", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@cypress/listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", - "requires": { - "chalk": "^1.1.3", - "cli-cursor": "^1.0.2", - "date-fns": "^1.27.2", - "figures": "^1.7.0" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "@cypress/request": { - "version": "2.88.5", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz", - "integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "requires": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "@samverschueren/stream-to-observable": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", - "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", - "requires": { - "any-observable": "^0.3.0" - } - }, - "@types/sinonjs__fake-timers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", - "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==" - }, - "@types/sizzle": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", - "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==" - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==" - }, - "arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==" - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - } - } - }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=" - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - } - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "optional": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" - }, - "common-tags": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", - "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "cypress": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-6.3.0.tgz", - "integrity": "sha512-Ec6TAFOxdSB2HPINNJ1f7z75pENXcfCaQkz+A9j0eGSvusFJ2NNErq650DexCbNJAnCQkPqXB4XPH9kXnSQnUA==", - "requires": { - "@cypress/listr-verbose-renderer": "^0.4.1", - "@cypress/request": "^2.88.5", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "^6.0.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.1.2", - "blob-util": "2.0.2", - "bluebird": "^3.7.2", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-table3": "~0.6.0", - "commander": "^5.1.0", - "common-tags": "^1.8.0", - "debug": "^4.1.1", - "eventemitter2": "^6.4.2", - "execa": "^4.0.2", - "executable": "^4.1.1", - "extract-zip": "^1.7.0", - "fs-extra": "^9.0.1", - "getos": "^3.2.1", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.2", - "lazy-ass": "^1.6.0", - "listr": "^0.14.3", - "lodash": "^4.17.19", - "log-symbols": "^4.0.0", - "minimist": "^1.2.5", - "moment": "^2.27.0", - "ospath": "^1.2.2", - "pretty-bytes": "^5.4.1", - "ramda": "~0.26.1", - "request-progress": "^3.0.0", - "supports-color": "^7.2.0", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "url": "^0.11.0", - "yauzl": "^2.10.0" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==" - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eventemitter2": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz", - "integrity": "sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ==" - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "dependencies": { - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - } - } - }, - "executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "requires": { - "pify": "^2.2.0" - } - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=" - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fs": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", - "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "requires": { - "async": "^3.2.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "requires": { - "ini": "1.3.7" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "requires": { - "symbol-observable": "^1.1.0" - } - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" - }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=" - }, - "listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - }, - "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - } - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=" - }, - "listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "requires": { - "chalk": "^1.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", - "requires": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "requires": { - "chalk": "^4.0.0" - } - }, - "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", - "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "dependencies": { - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" - }, - "mime-types": { - "version": "2.1.28", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", - "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", - "requires": { - "mime-db": "1.45.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" - }, - "ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=" - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "pretty-bytes": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.5.0.tgz", - "integrity": "sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA==" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "requires": { - "throttleit": "^1.0.0" - } - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=" - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "requires": { - "rimraf": "^3.0.0" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - } -} diff --git a/gitops/terraform/base/cypress/package.json b/gitops/terraform/base/cypress/package.json deleted file mode 100644 index bd3e743b6..000000000 --- a/gitops/terraform/base/cypress/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "gitlab-init", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "kubefirst", - "license": "MIT", - "dependencies": { - "cypress": "^6.3.0", - "fs": "0.0.1-security" - } -} diff --git a/gitops/terraform/base/dynamodb/output.tf b/gitops/terraform/base/dynamodb/output.tf deleted file mode 100644 index 6a726ed59..000000000 --- a/gitops/terraform/base/dynamodb/output.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "vault_table_id" { - value = aws_dynamodb_table.vault_dynamodb_table.id -} diff --git a/gitops/terraform/base/dynamodb/vault.tf b/gitops/terraform/base/dynamodb/vault.tf deleted file mode 100644 index 69fc2df51..000000000 --- a/gitops/terraform/base/dynamodb/vault.tf +++ /dev/null @@ -1,83 +0,0 @@ -resource "aws_appautoscaling_target" "dynamodb-table-read-target" { - max_capacity = 100 - min_capacity = 10 - resource_id = "table/${aws_dynamodb_table.vault_dynamodb_table.name}" - scalable_dimension = "dynamodb:table:ReadCapacityUnits" - service_namespace = "dynamodb" -} - -resource "aws_appautoscaling_policy" "dynamodb-table-read-policy" { - name = "DynamoDBReadCapacityUtilization:${aws_appautoscaling_target.dynamodb-table-read-target.resource_id}" - policy_type = "TargetTrackingScaling" - resource_id = aws_appautoscaling_target.dynamodb-table-read-target.resource_id - scalable_dimension = aws_appautoscaling_target.dynamodb-table-read-target.scalable_dimension - service_namespace = aws_appautoscaling_target.dynamodb-table-read-target.service_namespace - - target_tracking_scaling_policy_configuration { - predefined_metric_specification { - predefined_metric_type = "DynamoDBReadCapacityUtilization" - } - - target_value = 30 - } -} - -resource "aws_appautoscaling_target" "dynamodb-table-write-target" { - max_capacity = 100 - min_capacity = 10 - resource_id = "table/${aws_dynamodb_table.vault_dynamodb_table.name}" - scalable_dimension = "dynamodb:table:WriteCapacityUnits" - service_namespace = "dynamodb" -} - -resource "aws_appautoscaling_policy" "dynamodb_table_write_policy" { - name = "DynamoDBWriteCapacityUtilization:${aws_appautoscaling_target.dynamodb-table-write-target.resource_id}" - policy_type = "TargetTrackingScaling" - resource_id = aws_appautoscaling_target.dynamodb-table-write-target.resource_id - scalable_dimension = aws_appautoscaling_target.dynamodb-table-write-target.scalable_dimension - service_namespace = aws_appautoscaling_target.dynamodb-table-write-target.service_namespace - - target_tracking_scaling_policy_configuration { - predefined_metric_specification { - predefined_metric_type = "DynamoDBWriteCapacityUtilization" - } - - target_value = 30 - } -} - -resource "aws_dynamodb_table" "vault_dynamodb_table" { - # todo https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table#point_in_time_recovery - name = "vault-dynamodb-backend" - billing_mode = "PROVISIONED" - read_capacity = 15 - write_capacity = 33 - hash_key = "Path" - range_key = "Key" - stream_enabled = false - point_in_time_recovery { - enabled = true - } - - attribute { - name = "Path" - type = "S" - } - - attribute { - name = "Key" - type = "S" - } - - tags = { - Name = "vault-dynamodb-backend" - VaultInstance = "vault" - Environment = "mgmt" - } - - lifecycle { - ignore_changes = [ - read_capacity, write_capacity - ] - } -} diff --git a/gitops/terraform/base/ec2/gitlab-vm.tf b/gitops/terraform/base/ec2/gitlab-vm.tf deleted file mode 100644 index 50c1c3c49..000000000 --- a/gitops/terraform/base/ec2/gitlab-vm.tf +++ /dev/null @@ -1,83 +0,0 @@ -resource "random_string" "random" { - length = 6 - special = false -} - -resource "aws_key_pair" "gitlab_public_key" { - key_name = "terraform-ssh-key-${random_string.random.result}" - public_key = file("${path.root}/terraform-ssh-key.pub") - -} - -data "aws_ami_ids" "ubuntu" { - owners = ["099720109477"] - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20200112"] - } - filter { - name = "virtualization-type" - values = ["hvm"] - } -} - -data "template_file" "gitlab_install_script" { - template = file("${path.module}/scripts/install_gitlab.sh") - vars = { - EMAIL_DOMAIN = var.email_domain - GITLAB_URL = var.gitlab_url - GITLAB_BOT_ROOT_PASSWORD = var.gitlab_bot_root_password - } -} - -resource "aws_instance" "gitlab" { - ami = data.aws_ami_ids.ubuntu.ids[0] - associate_public_ip_address = true - availability_zone = "${var.aws_region}a" - disable_api_termination = false - ebs_optimized = false - instance_type = "t3.large" - ipv6_address_count = 0 - ipv6_addresses = [] - key_name = aws_key_pair.gitlab_public_key.key_name - monitoring = false - source_dest_check = true - subnet_id = var.vpc_public_subnet - tenancy = "default" # warning: replace if compliance requirements dictate non-shared resources - user_data = data.template_file.gitlab_install_script.rendered - lifecycle { - ignore_changes = [ - user_data - ] - } - volume_tags = { - "Name" = "gitlab" - } - vpc_security_group_ids = [ - var.vpc_default_sg_id, - var.gitlab_sg_id, - ] - metadata_options { - http_endpoint = "enabled" - http_put_response_hop_limit = 1 - http_tokens = "optional" - } - root_block_device { - delete_on_termination = true - encrypted = false - volume_size = 30 - volume_type = "gp2" - } - tags = { - "Name" = "gitlab" - } -} -resource "aws_route53_record" "gitlab" { - zone_id = var.hosted_zone_id - name = var.gitlab_url - type = "A" - ttl = "300" - records = [aws_instance.gitlab.public_ip] - - depends_on = [aws_instance.gitlab] -} diff --git a/gitops/terraform/base/ec2/outputs.tf b/gitops/terraform/base/ec2/outputs.tf deleted file mode 100644 index dd7e17e1d..000000000 --- a/gitops/terraform/base/ec2/outputs.tf +++ /dev/null @@ -1,3 +0,0 @@ -output "gitlab_public_ip" { - value = aws_instance.gitlab.public_ip -} diff --git a/gitops/terraform/base/ec2/scripts/install_gitlab.sh b/gitops/terraform/base/ec2/scripts/install_gitlab.sh deleted file mode 100755 index a9b9f95a7..000000000 --- a/gitops/terraform/base/ec2/scripts/install_gitlab.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -sudo apt-get update -sudo apt-get install -y curl openssh-server ca-certificates -​ -sudo debconf-set-selections <<< "postfix postfix/mailname string ${EMAIL_DOMAIN}" -sudo debconf-set-selections <<< "postfix postfix/main_mailer_type string 'Internet Site'" -​ -sudo apt-get install --assume-yes postfix -​ -curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash - - -# to unpin, remove the version from the end of the command as shown here -# sudo EXTERNAL_URL="https://${GITLAB_URL}" GITLAB_ROOT_PASSWORD="${GITLAB_BOT_ROOT_PASSWORD}" apt-get install gitlab-ce -# for list of releases see: https://about.gitlab.com/releases/categories/releases/ -sudo EXTERNAL_URL="https://${GITLAB_URL}" GITLAB_ROOT_PASSWORD="${GITLAB_BOT_ROOT_PASSWORD}" apt-get install gitlab-ce=14.9.2-ce.0 diff --git a/gitops/terraform/base/ec2/variables.tf b/gitops/terraform/base/ec2/variables.tf deleted file mode 100644 index b03dbccf9..000000000 --- a/gitops/terraform/base/ec2/variables.tf +++ /dev/null @@ -1,31 +0,0 @@ -variable "vpc_default_sg_id" { - type = string -} - -variable "vpc_public_subnet" { - type = string -} - -variable "gitlab_sg_id" { - type = string -} - -variable "aws_region" { - type = string -} - -variable "gitlab_bot_root_password" { - type = string -} - -variable "gitlab_url" { - type = string -} - -variable "email_domain" { - type = string -} - -variable "hosted_zone_id" { - type = string -} diff --git a/gitops/terraform/base/eks/main.tf b/gitops/terraform/base/eks/main.tf deleted file mode 100644 index edbbcee50..000000000 --- a/gitops/terraform/base/eks/main.tf +++ /dev/null @@ -1,267 +0,0 @@ -terraform { - required_version = ">= 0.12.0" -} - -provider "random" { - version = "~> 2.1" -} - -provider "local" { - version = "~> 1.2" -} - -provider "null" { - version = "~> 2.1" -} - -provider "template" { - version = "~> 2.1" -} - -data "aws_eks_cluster" "cluster" { - name = module.eks.cluster_id -} - -data "aws_eks_cluster_auth" "cluster" { - name = module.eks.cluster_id -} - -provider "kubernetes" { - host = data.aws_eks_cluster.cluster.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data) - token = data.aws_eks_cluster_auth.cluster.token - load_config_file = false - version = "~> 1.11" -} - -data "aws_availability_zones" "available" { -} - -locals { - cluster_name = var.cluster_name -} - -resource "random_string" "suffix" { - length = 8 - special = false -} - -resource "aws_security_group" "worker_group_mgmt_one" { - name_prefix = "worker_group_mgmt_one" - vpc_id = module.vpc.vpc_id - - ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - - cidr_blocks = [ - "10.0.0.0/8", - ] - } -} - -resource "aws_security_group" "worker_group_mgmt_two" { - name_prefix = "worker_group_mgmt_two" - vpc_id = module.vpc.vpc_id - - ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - - cidr_blocks = [ - "192.168.0.0/16", - ] - } -} - -resource "aws_security_group" "all_worker_mgmt" { - name_prefix = "all_worker_management" - vpc_id = module.vpc.vpc_id - - ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - - cidr_blocks = [ - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/16", - ] - } -} -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - version = "2.47.0" - - name = "kubefirst-vpc" - cidr = "10.0.0.0/16" - azs = data.aws_availability_zones.available.names - private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] - public_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"] - enable_nat_gateway = true - single_nat_gateway = true - enable_dns_hostnames = true - - public_subnet_tags = { - "kubernetes.io/cluster/${local.cluster_name}" = "shared" - "kubernetes.io/role/elb" = "1" - } - - private_subnet_tags = { - "kubernetes.io/cluster/${local.cluster_name}" = "shared" - "kubernetes.io/role/internal-elb" = "1" - } -} - -module "eks" { - version = "17.20.0" - source = "terraform-aws-modules/eks/aws" - cluster_name = local.cluster_name - cluster_version = "1.20" - subnets = module.vpc.private_subnets - - tags = { - ClusterName = "kubefirst" - } - - vpc_id = module.vpc.vpc_id - - map_roles = concat(var.map_roles, [{ - rolearn = "arn:aws:iam::${var.aws_account_id}:role/KubernetesAdmin" - username = "admin" - groups = ["system:masters"] - }, { - rolearn = aws_iam_role.kubefirst_worker_nodes_role.arn - username = "system:node:{{EC2PrivateDNSName}}" - groups = ["system:bootstrappers", "system:nodes"] - }]) - map_users = concat(var.map_users, [{ - userarn = var.iam_user_arn - username = "admin" - groups = ["system:masters"] - }]) - map_accounts = var.map_accounts -} - - -resource "aws_eks_node_group" "preprod_nodes" { - cluster_name = module.eks.cluster_id - node_group_name = "preprod-nodes" - node_role_arn = aws_iam_role.kubefirst_worker_nodes_role.arn - subnet_ids = module.vpc.private_subnets - ami_type = "AL2_x86_64" - disk_size = 50 - - labels = { - "workload" = "preprod" - } - - scaling_config { - desired_size = 2 - max_size = 2 - min_size = 2 - } - - # Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling. - # Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces. - depends_on = [ - module.eks - ] -} - -resource "aws_eks_node_group" "mgmt_nodes" { - cluster_name = module.eks.cluster_id - node_group_name = "mgmt-nodes" - node_role_arn = aws_iam_role.kubefirst_worker_nodes_role.arn - subnet_ids = module.vpc.private_subnets - ami_type = "AL2_x86_64" - disk_size = 50 - - labels = { - "workload" = "mgmt" - } - - scaling_config { - desired_size = 1 - max_size = 1 - min_size = 1 - } - - # Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling. - # Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces. - depends_on = [ - module.eks - ] -} -resource "aws_eks_node_group" "production_nodes" { - cluster_name = module.eks.cluster_id - node_group_name = "production-nodes" - node_role_arn = aws_iam_role.kubefirst_worker_nodes_role.arn - subnet_ids = module.vpc.private_subnets - ami_type = "AL2_x86_64" - disk_size = 50 - - labels = { - "workload" = "production" - } - - scaling_config { - desired_size = 1 - max_size = 1 - min_size = 1 - } - - # Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling. - # Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces. - depends_on = [ - module.eks - ] -} - -resource "random_string" "random" { - length = 16 - special = false -} - -resource "aws_iam_role" "kubefirst_worker_nodes_role" { - name = "kubefirst-worker-nodes-role-${random_string.random.result}" - - assume_role_policy = < - -context('Window', () => { - before(() => { - cy.visit('/') - }) - - it('logs in with root user', () => { - cy.get('#user_login') - .type(Cypress.env('gitlab_bot_username_before')) - cy.get('#user_password') - .type(Cypress.env('gitlab_bot_password')) - cy.get('.gl-button').click() - }) - - it('sets up a personal access token', () => { - cy.visit('/-/profile/personal_access_tokens') - cy.get('#personal_access_token_name').type('kubefirst') - cy.get('#personal_access_token_scopes_api').check() - cy.get('#personal_access_token_scopes_write_repository').check() - cy.get('#personal_access_token_scopes_write_registry').check() - cy.get('#new_personal_access_token > .gl-mt-3 > .gl-button').click() - cy.get('#created-personal-access-token').then(elem => { - // elem is the underlying Javascript object targeted by the .get() command. - const token = Cypress.$(elem).val(); - cy.writeFile('../.gitlab-bot-access-token', token) - }) - }) - - it('gets the runner registration token', () => { - cy.visit('/admin/runners') - cy.get('#__BVID__33__BV_toggle_ > [data-testid=chevron-down-icon]').click() - cy.get('[data-testid=eye-icon] > use').click() - cy.get('[data-testid=token-value] > .gl-button-text').then(elem => { - // elem is the underlying Javascript object targeted by the .get() command. - const token = Cypress.$(elem).text(); - cy.writeFile('../.gitlab-runner-registration-token', token.trim()) - }) - }) - -}) diff --git a/gitops/terraform/cypress/cypress/plugins/index.js b/gitops/terraform/cypress/cypress/plugins/index.js deleted file mode 100644 index aa9918d21..000000000 --- a/gitops/terraform/cypress/cypress/plugins/index.js +++ /dev/null @@ -1,21 +0,0 @@ -/// -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -} diff --git a/gitops/terraform/cypress/cypress/support/commands.js b/gitops/terraform/cypress/cypress/support/commands.js deleted file mode 100644 index ca4d256f3..000000000 --- a/gitops/terraform/cypress/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/gitops/terraform/cypress/cypress/support/index.js b/gitops/terraform/cypress/cypress/support/index.js deleted file mode 100644 index f6d888f00..000000000 --- a/gitops/terraform/cypress/cypress/support/index.js +++ /dev/null @@ -1,29 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') -Cypress.Cookies.defaults({ - preserve: ['_gitlab_session', 'known_sign_in'] -}) - -Cypress.on('uncaught:exception', (err, runnable) => { - // returning false here prevents Cypress from - // failing the test - return false -}) diff --git a/gitops/terraform/cypress/package-lock.json b/gitops/terraform/cypress/package-lock.json deleted file mode 100644 index 2e64b5fa4..000000000 --- a/gitops/terraform/cypress/package-lock.json +++ /dev/null @@ -1,1523 +0,0 @@ -{ - "name": "gitlab-init", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@cypress/listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", - "requires": { - "chalk": "^1.1.3", - "cli-cursor": "^1.0.2", - "date-fns": "^1.27.2", - "figures": "^1.7.0" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "@cypress/request": { - "version": "2.88.10", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", - "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^8.3.2" - } - }, - "@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "requires": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "@samverschueren/stream-to-observable": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", - "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", - "requires": { - "any-observable": "^0.3.0" - } - }, - "@types/sinonjs__fake-timers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", - "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==" - }, - "@types/sizzle": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", - "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==" - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==" - }, - "arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==" - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==" - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - } - } - }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=" - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - } - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "optional": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" - }, - "common-tags": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", - "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "cypress": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-6.3.0.tgz", - "integrity": "sha512-Ec6TAFOxdSB2HPINNJ1f7z75pENXcfCaQkz+A9j0eGSvusFJ2NNErq650DexCbNJAnCQkPqXB4XPH9kXnSQnUA==", - "requires": { - "@cypress/listr-verbose-renderer": "^0.4.1", - "@cypress/request": "^2.88.5", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "^6.0.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.1.2", - "blob-util": "2.0.2", - "bluebird": "^3.7.2", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-table3": "~0.6.0", - "commander": "^5.1.0", - "common-tags": "^1.8.0", - "debug": "^4.1.1", - "eventemitter2": "^6.4.2", - "execa": "^4.0.2", - "executable": "^4.1.1", - "extract-zip": "^1.7.0", - "fs-extra": "^9.0.1", - "getos": "^3.2.1", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.2", - "lazy-ass": "^1.6.0", - "listr": "^0.14.3", - "lodash": "^4.17.19", - "log-symbols": "^4.0.0", - "minimist": "^1.2.5", - "moment": "^2.27.0", - "ospath": "^1.2.2", - "pretty-bytes": "^5.4.1", - "ramda": "~0.26.1", - "request-progress": "^3.0.0", - "supports-color": "^7.2.0", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "url": "^0.11.0", - "yauzl": "^2.10.0" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==" - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eventemitter2": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz", - "integrity": "sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ==" - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "dependencies": { - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - } - } - }, - "executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "requires": { - "pify": "^2.2.0" - } - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=" - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fs": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", - "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "requires": { - "async": "^3.2.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "requires": { - "ini": "1.3.7" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "requires": { - "symbol-observable": "^1.1.0" - } - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" - }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=" - }, - "listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - }, - "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - } - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=" - }, - "listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "requires": { - "chalk": "^1.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", - "requires": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "requires": { - "chalk": "^4.0.0" - } - }, - "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", - "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "dependencies": { - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" - }, - "ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=" - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "pretty-bytes": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.5.0.tgz", - "integrity": "sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA==" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "requires": { - "throttleit": "^1.0.0" - } - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=" - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=" - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "requires": { - "rimraf": "^3.0.0" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - } -} diff --git a/gitops/terraform/cypress/package.json b/gitops/terraform/cypress/package.json deleted file mode 100644 index bd3e743b6..000000000 --- a/gitops/terraform/cypress/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "gitlab-init", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "kubefirst", - "license": "MIT", - "dependencies": { - "cypress": "^6.3.0", - "fs": "0.0.1-security" - } -} diff --git a/gitops/terraform/gitlab/kubefirst-repos.tf b/gitops/terraform/gitlab/kubefirst-repos.tf deleted file mode 100644 index c486756ea..000000000 --- a/gitops/terraform/gitlab/kubefirst-repos.tf +++ /dev/null @@ -1,53 +0,0 @@ -terraform { - backend "s3" { - bucket = "" - key = "terraform/gitlab/tfstate.tf" - region = "" - encrypt = true - } -} - -resource "gitlab_group" "kubefirst" { - name = "kubefirst" - path = "kubefirst" - description = "a private group for kubefirst repositories" - request_access_enabled = false - visibility_level = "private" -} - -module "metaphor" { - depends_on = [ - gitlab_group.kubefirst - ] - source = "./templates/gitlab-repo" - group_name = gitlab_group.kubefirst.id - repo_name = "metaphor" - create_ecr = true - initialize_with_readme = false - only_allow_merge_if_pipeline_succeeds = false - remove_source_branch_after_merge = true -} - -module "gitops" { - depends_on = [ - gitlab_group.kubefirst - ] - source = "./templates/gitlab-repo" - group_name = gitlab_group.kubefirst.id - repo_name = "gitops" - create_ecr = true - initialize_with_readme = false - only_allow_merge_if_pipeline_succeeds = false - remove_source_branch_after_merge = true -} - -resource "gitlab_project_hook" "atlantis" { - depends_on = [ - module.gitops - ] - project = "kubefirst/gitops" - url = "https://atlantis./events" - merge_requests_events = true - push_events = true - note_events = true -} diff --git a/gitops/terraform/gitlab/templates/gitlab-repo/main.tf b/gitops/terraform/gitlab/templates/gitlab-repo/main.tf deleted file mode 100644 index feba93d38..000000000 --- a/gitops/terraform/gitlab/templates/gitlab-repo/main.tf +++ /dev/null @@ -1,27 +0,0 @@ -# -# todo - need to talk about what else we want to be a part of repo creation? -# https://www.terraform.io/docs/providers/gitlab/r/project.html#argument-reference -resource "gitlab_project" "repo" { - name = var.repo_name - archived = var.archived - visibility_level = "private" - default_branch = var.default_branch - namespace_id = var.group_name - import_url = var.import_url - initialize_with_readme = var.initialize_with_readme - shared_runners_enabled = true - # https://docs.gitlab.com/ee/user/packages/container_registry/ - only_allow_merge_if_all_discussions_are_resolved = true - only_allow_merge_if_pipeline_succeeds = var.only_allow_merge_if_pipeline_succeeds - remove_source_branch_after_merge = var.remove_source_branch_after_merge -} - -resource "aws_ecr_repository" "ecr_repo" { - count = var.create_ecr != true ? 0 : 1 - name = var.repo_name - image_tag_mutability = "IMMUTABLE" - - image_scanning_configuration { - scan_on_push = true - } -} diff --git a/gitops/terraform/gitlab/templates/gitlab-repo/outputs.tf b/gitops/terraform/gitlab/templates/gitlab-repo/outputs.tf deleted file mode 100644 index 7d556f4d9..000000000 --- a/gitops/terraform/gitlab/templates/gitlab-repo/outputs.tf +++ /dev/null @@ -1,4 +0,0 @@ -output "repo_url" { - value = gitlab_project.repo.web_url - description = "gitlab project url" -} diff --git a/gitops/terraform/gitlab/templates/gitlab-repo/variables.tf b/gitops/terraform/gitlab/templates/gitlab-repo/variables.tf deleted file mode 100644 index 72ecdbdaf..000000000 --- a/gitops/terraform/gitlab/templates/gitlab-repo/variables.tf +++ /dev/null @@ -1,53 +0,0 @@ -variable "archived" { - description = "whether to archive the repo (make it readonly)" - type = bool - default = false -} -variable "group_name" { - description = "the group name the repository belongs to" - type = string -} - -variable "repo_name" { - description = "the name of the repository" - type = string -} - -variable "create_deploy_key" { - description = "whether or not to create a deploy key for ci to commit to the repo" - type = bool - default = false -} - -variable "create_ecr" { - description = "whether or not to create the ecr repository" - type = bool -} - -variable "only_allow_merge_if_pipeline_succeeds" { - description = "set to true once your branch or mr has a successful pipeline you can depend on" - type = bool -} - -variable "remove_source_branch_after_merge" { - description = "whether or not we should remove source branch after a merge" - type = bool -} - -variable "default_branch" { - description = "specifies what the default branch is for the repository" - type = string - default = "main" -} - -variable "import_url" { - description = "import url of the git repository" - type = string - default = null -} - -variable "initialize_with_readme" { - description = "whether or not to add a readme at project creation" - type = bool - default = true -} diff --git a/gitops/terraform/gitlab/templates/gitlab-repo/versions.tf b/gitops/terraform/gitlab/templates/gitlab-repo/versions.tf deleted file mode 100644 index 9ffb9bfac..000000000 --- a/gitops/terraform/gitlab/templates/gitlab-repo/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - } - gitlab = { - source = "gitlabhq/gitlab" - } - } -} diff --git a/gitops/terraform/gitlab/versions.tf b/gitops/terraform/gitlab/versions.tf deleted file mode 100644 index ec94f9a09..000000000 --- a/gitops/terraform/gitlab/versions.tf +++ /dev/null @@ -1,7 +0,0 @@ -terraform { - required_providers { - gitlab = { - source = "gitlabhq/gitlab" - } - } -} diff --git a/gitops/terraform/users/admin.tf b/gitops/terraform/users/admin.tf deleted file mode 100644 index dfe33ccdc..000000000 --- a/gitops/terraform/users/admin.tf +++ /dev/null @@ -1,25 +0,0 @@ -resource "gitlab_group" "admins" { - name = "admins" - path = "admins" - description = "admins group" -} - -module "admin_one" { - source = "./templates/oidc-user" - admins_group_id = gitlab_group.admins.id - developer_group_id = gitlab_group.developer.id - username = "admin1" - fullname = "Admin One" - email = "admin1@yourcompany.com" - is_admin = true -} - -module "admin_two" { - source = "./templates/oidc-user" - admins_group_id = gitlab_group.admins.id - developer_group_id = gitlab_group.developer.id - username = "admin2" - fullname = "Admin Two" - email = "admin2@yourcompany.com" - is_admin = true -} diff --git a/gitops/terraform/users/backend.tf b/gitops/terraform/users/backend.tf deleted file mode 100644 index c6852e78c..000000000 --- a/gitops/terraform/users/backend.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - backend "s3" { - bucket = "" - key = "terraform/users/tfstate.tf" - region = "" - encrypt = true - } -} \ No newline at end of file diff --git a/gitops/terraform/users/developers.tf b/gitops/terraform/users/developers.tf deleted file mode 100644 index 63c47f865..000000000 --- a/gitops/terraform/users/developers.tf +++ /dev/null @@ -1,23 +0,0 @@ -resource "gitlab_group" "developer" { - name = "developer" - path = "developer" - description = "developer group" -} - -module "developer_one" { - source = "./templates/oidc-user" - admins_group_id = gitlab_group.admins.id - developer_group_id = gitlab_group.developer.id - username = "developer1" - fullname = "Developer One" - email = "developer1@yourcompany.com" -} - -module "developer_two" { - source = "./templates/oidc-user" - admins_group_id = gitlab_group.admins.id - developer_group_id = gitlab_group.developer.id - username = "developer2" - fullname = "Developer Two" - email = "developer2@yourcompany.com" -} diff --git a/gitops/terraform/users/templates/oidc-user/user.tf b/gitops/terraform/users/templates/oidc-user/user.tf deleted file mode 100644 index e4e39d6a3..000000000 --- a/gitops/terraform/users/templates/oidc-user/user.tf +++ /dev/null @@ -1,70 +0,0 @@ -variable username { - type = string - description = "a distinct username that is unique to this user throughout the kubefirst ecosystem" -} -variable fullname { - type = string - description = "example: Jane Doe" -} -variable email { - type = string - description = "jane.doe@yourdomain.com" -} -variable is_admin { - default = false - description = "setting to true will add the user to the admins group, granting them admin access to our apps. setting to false will add the user to the developer group, granting them developer access to our apps" -} -variable enabled { - default = true - description = "setting to false allows you to destroy all resources so you can cleanly remove the user before removing them from terraform" -} -variable admins_group_id { - type = string - description = "for admin group assignment when is_admin" -} -variable developer_group_id { - type = string - description = "for developer group assignment when not is_admin" -} - -resource "random_password" "user" { - length = 16 - special = true - override_special = "_#!" -} - -resource "gitlab_user" "user" { - count = var.enabled ? 1 : 0 - name = var.fullname - username = var.username - password = random_password.user.result - email = var.email - is_admin = var.is_admin - projects_limit = 100 - can_create_group = true - is_external = false - reset_password = false - # initial gitlab password are stored in vault. to allow gitlab to manage passwords, - # you should remove `password` and change `reset_password` to true. however, you'll need to - # enable gitlab email before setting to reset_password to true. see this link for config settings: - # https://github.com/gitlabhq/omnibus-gitlab/blob/master/doc/settings/smtp.md - # we didn't want this dependency on kubefirst's initial setup due to the variations in how companies - # manage email. if you don't have company email available to you, the gmail integration works well. -} - -resource "gitlab_group_membership" "user_admin_group" { - count = var.enabled ? 1 : 0 - group_id = var.is_admin ? var.admins_group_id : var.developer_group_id - user_id = gitlab_user.user[count.index].id - access_level = "maintainer" -} - -resource "vault_generic_secret" "user_password" { - count = var.enabled ? 1 : 0 # keep secret in vault if user is enabled - path = "users/${gitlab_user.user[count.index].username}" - data_json = <:443", - "ARGO_SERVER_URL": "argo.:443", - "ATLANTIS_GITLAB_HOSTNAME": "gitlab.", - "ATLANTIS_GITLAB_TOKEN": "${var.atlantis_gitlab_token}", - "ATLANTIS_GITLAB_USER": "kubefirst", - "ATLANTIS_GITLAB_WEBHOOK_SECRET": "${var.atlantis_gitlab_webhook_secret}", - "AWS_ACCESS_KEY_ID": "${var.aws_access_key_id}", - "AWS_DEFAULT_REGION": "", - "AWS_ROLE_TO_ASSUME": "arn:aws:iam:::role/KubernetesAdmin", - "AWS_SECRET_ACCESS_KEY": "${var.aws_secret_access_key}", - "AWS_SESSION_NAME": "GitHubAction", - "GITLAB_BASE_URL": "https://gitlab.", - "GITLAB_TOKEN": "${var.gitlab_token}", - "KEYCLOAK_CLIENT_ID": "admin-cli", - "KEYCLOAK_PASSWORD": "${var.keycloak_password}", - "KEYCLOAK_REALM": "master", - "KEYCLOAK_URL": "https://keycloak.", - "KEYCLOAK_USER": "gitlab-bot", - "KUBECONFIG": "/.kube/config", - "TF_VAR_argo_redirect_uris": "[\"https://argo./oauth2/callback\"]", - "TF_VAR_argocd_auth_password": "${var.argocd_auth_password}", - "TF_VAR_argocd_redirect_uris": "[\"https://argocd./auth/callback\",\"https://argocd./applications\"]", - "TF_VAR_atlantis_gitlab_token": "${var.atlantis_gitlab_token}", - "TF_VAR_atlantis_gitlab_webhook_secret": "${var.atlantis_gitlab_webhook_secret}", - "TF_VAR_aws_access_key_id": "${var.aws_access_key_id}", - "TF_VAR_aws_account_id": "", - "TF_VAR_aws_secret_access_key": "${var.aws_secret_access_key}", - "TF_VAR_aws_region": "", - "TF_VAR_email_address": "${var.email_address}", - "TF_VAR_email_domain": "${var.email_domain}", - "TF_VAR_gitlab_bot_root_password": "${var.gitlab_token}", - "TF_VAR_gitlab_redirect_uris": "[\"https://gitlab.\"]", - "TF_VAR_gitlab_runner_token": "${var.gitlab_runner_token}", - "TF_VAR_gitlab_token": "${var.gitlab_token}", - "TF_VAR_gitlab_url": "gitlab.", - "TF_VAR_hosted_zone_id": "${var.hosted_zone_id}", - "TF_VAR_hosted_zone_name": "${var.hosted_zone_name}", - "TF_VAR_iam_user_arn": "${var.iam_user_arn}", - "TF_VAR_keycloak_admin_password": "${var.keycloak_admin_password}", - "TF_VAR_keycloak_password": "${var.keycloak_password}", - "TF_VAR_vault_addr": "${var.vault_addr}", - "TF_VAR_vault_redirect_uris": "[\"https://vault./ui/vault/auth/oidc/oidc/callback\",\"http://localhost:8200/ui/vault/auth/oidc/oidc/callback\",\"http://localhost:8250/oidc/callback\",\"https://vault.:8250/oidc/callback\"]", - "TF_VAR_vault_token": "${var.vault_token}", - "VAULT_ADDR": "https://vault.", - "VAULT_TOKEN": "${var.vault_token}" -} -EOT -} - -resource "vault_generic_secret" "development_metaphor" { - path = "${vault_mount.secret.path}/development/metaphor" - # note: these secrets are not actually sensitive. - # do not hardcode passwords in git under normal circumstances. - data_json = < - - name: chartDir - value: charts/{{workflow.parameters.appName}} - - name: ciCommitSha - value: "{{workflow.parameters.ciCommitSha}}" - - name: branch - value: "{{workflow.parameters.branch}}" - - name: environment - value: "{{workflow.parameters.environment}}" - - name: fullChartPath - value: "components/{{workflow.parameters.environment}}/{{workflow.parameters.appName}}/Chart.yaml" - - name: gitRepoUrl - value: "{{workflow.parameters.gitRepoUrl}}" - - name: shortSha - value: "{{workflow.parameters.shortSha}}" - templates: - - name: main - steps: - - - name: checkout - templateRef: - name: cwft-git - template: git-checkout-with-gitops - clusterScope: true - arguments: - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: branch - value: "{{workflow.parameters.branch}}" - - name: gitRepoUrl - value: "{{workflow.parameters.gitRepoUrl}}" - - - - name: get-initial-chart-version - templateRef: - name: cwft-helm - template: helm-get-chart-version - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: chartDir - value: "{{workflow.parameters.chartDir}}" - - - - name: set-environment-version - templateRef: - name: cwft-helm - template: helm-set-environment-version - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: fullChartPath - value: "{{workflow.parameters.fullChartPath}}" - - name: chartVersion - value: "{{steps.get-initial-chart-version.outputs.result}}-rc.{{workflow.parameters.shortSha}}" - - name: environment - value: "{{workflow.parameters.environment}}" - - - - name: commit - templateRef: - name: cwft-git - template: git-commit - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.set-environment-version.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "/src/gitops" - - name: commitMessage - value: setting {{workflow.parameters.appName}} {{workflow.parameters.environment}} to chart version {{steps.get-initial-chart-version.outputs.result}}-rc.{{workflow.parameters.shortSha}} - diff --git a/metaphor/.argo/npm-run.yaml b/metaphor/.argo/npm-run.yaml deleted file mode 100644 index 4513959d7..000000000 --- a/metaphor/.argo/npm-run.yaml +++ /dev/null @@ -1,59 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -spec: - entrypoint: main - arguments: - parameters: - - name: appDir - value: /src/{{workflow.parameters.appName}} - - name: branch - value: "{{workflow.parameters.branch}}" - - name: gitRepoUrl - value: "{{workflow.parameters.gitRepoUrl}}" - - name: npmScriptName - value: "{{workflow.parameters.npmScriptName}}" - templates: - - name: main - steps: - - - name: checkout - templateRef: - name: cwft-git - template: git-checkout-with-gitops - clusterScope: true - arguments: - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: branch - value: "{{workflow.parameters.branch}}" - - name: gitRepoUrl - value: "{{workflow.parameters.gitRepoUrl}}" - - - - name: npm-install - templateRef: - name: cwft-npm - template: npm-install - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - - - name: npm-run - templateRef: - name: cwft-npm - template: npm-run - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.npm-install.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: npmRunScript - value: "{{workflow.parameters.npmScriptName}}" - \ No newline at end of file diff --git a/metaphor/.argo/publish.yaml b/metaphor/.argo/publish.yaml deleted file mode 100644 index baeb8a27d..000000000 --- a/metaphor/.argo/publish.yaml +++ /dev/null @@ -1,102 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -spec: - entrypoint: main - arguments: - parameters: - - name: appDir - value: /src/{{workflow.parameters.appName}} - - name: appName - value: "{{workflow.parameters.appName}}" - - name: chartDir - value: charts/{{workflow.parameters.appName}} - - name: ciCommitSha - value: "{{workflow.parameters.ciCommitSha}}" - - name: branch - value: "{{workflow.parameters.branch}}" - - name: gitRepoUrl - value: "{{workflow.parameters.gitRepoUrl}}" - - name: shortSha - value: "{{workflow.parameters.shortSha}}" - templates: - - name: main - steps: - - - name: checkout - templateRef: - name: cwft-git - template: git-checkout-with-gitops - clusterScope: true - arguments: - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: branch - value: "{{workflow.parameters.branch}}" - - name: gitRepoUrl - value: "{{workflow.parameters.gitRepoUrl}}" - - - - name: publish-container - templateRef: - name: cwft-docker - template: docker-build - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: appName - value: "{{workflow.parameters.appName}}" - - name: ciCommitSha - value: "{{workflow.parameters.ciCommitSha}}" - - - - name: get-initial-chart-version - templateRef: - name: cwft-helm - template: helm-get-chart-version - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: chartDir - value: "{{workflow.parameters.chartDir}}" - - - - name: set-chart-versions - templateRef: - name: cwft-helm - template: helm-set-chart-versions - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: chartDir - value: "{{workflow.parameters.chartDir}}" - - name: chartVersion - value: "{{steps.get-initial-chart-version.outputs.result}}-rc.{{workflow.parameters.shortSha}}" - - name: ciCommitSha - value: "{{workflow.parameters.ciCommitSha}}" - - - - name: publish-helm-chart - templateRef: - name: cwft-helm - template: helm-publish-chart - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.set-chart-versions.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: chartDir - value: "{{workflow.parameters.chartDir}}" diff --git a/metaphor/.argo/release.yaml b/metaphor/.argo/release.yaml deleted file mode 100644 index 31acc9daa..000000000 --- a/metaphor/.argo/release.yaml +++ /dev/null @@ -1,159 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Workflow -spec: - entrypoint: main - arguments: - parameters: - - name: appDir - value: /src/{{workflow.parameters.appName}} - - name: appName - value: "{{workflow.parameters.appName}}" - - name: awsRegion - value: - - name: branch - value: "{{workflow.parameters.branch}}" - - name: chartDir - value: charts/{{workflow.parameters.appName}} - - name: ciCommitSha - value: "{{workflow.parameters.ciCommitSha}}" - - name: environment - value: "{{workflow.parameters.environment}}" - - name: fullChartPath - value: "components/{{workflow.parameters.environment}}/{{workflow.parameters.appName}}/Chart.yaml" - - name: gitRepoUrl - value: "{{workflow.parameters.gitRepoUrl}}" - - name: shortSha - value: "{{workflow.parameters.shortSha}}" - - templates: - - name: main - steps: - - - name: checkout - templateRef: - name: cwft-git - template: git-checkout-with-gitops - clusterScope: true - arguments: - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: branch - value: "{{workflow.parameters.branch}}" - - name: gitRepoUrl - value: "{{workflow.parameters.gitRepoUrl}}" - - - - name: get-initial-chart-version - templateRef: - name: cwft-helm - template: helm-get-chart-version - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: chartDir - value: "{{workflow.parameters.chartDir}}" - - - - name: set-chart-versions - templateRef: - name: cwft-helm - template: helm-set-chart-versions - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: chartDir - value: "{{workflow.parameters.chartDir}}" - - name: chartVersion - value: "{{steps.get-initial-chart-version.outputs.result}}" - - name: ciCommitSha - value: "{{workflow.parameters.ciCommitSha}}" - - - - name: publish-helm-chart - templateRef: - name: cwft-helm - template: helm-publish-chart - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.set-chart-versions.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: chartDir - value: "{{workflow.parameters.chartDir}}" - - - - name: set-environment-version - templateRef: - name: cwft-helm - template: helm-set-environment-version - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.checkout.outputs.artifacts.repo-source}}" - parameters: - - name: fullChartPath - value: "{{workflow.parameters.fullChartPath}}" - - name: chartVersion - value: "{{steps.get-initial-chart-version.outputs.result}}" - - name: environment - value: "{{workflow.parameters.environment}}" - - - - name: commit-production - templateRef: - name: cwft-git - template: git-commit - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.set-environment-version.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "/src/gitops" - - name: commitMessage - value: "setting {{workflow.parameters.appName}} {{workflow.parameters.environment}} to chart version {{steps.get-initial-chart-version.outputs.result}}" - - - - name: increment-chart-patch - templateRef: - name: cwft-helm - template: helm-increment-chart-patch - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.set-environment-version.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: chartDir - value: "{{workflow.parameters.chartDir}}" - - name: chartVersion - value: "{{steps.get-initial-chart-version.outputs.result}}" - - - - name: commit-chart-increment - templateRef: - name: cwft-git - template: git-commit - clusterScope: true - arguments: - artifacts: - - name: repo-source - from: "{{steps.increment-chart-patch.outputs.artifacts.repo-source}}" - parameters: - - name: appDir - value: "{{workflow.parameters.appDir}}" - - name: commitMessage - value: "[CI SKIP] setting {{workflow.parameters.appName}} {{workflow.parameters.environment}} to chart version {{steps.get-initial-chart-version.outputs.result}}" - - name: repoPath - value: gitlab./kubefirst/{{workflow.parameters.appName}} - diff --git a/metaphor/.gitignore b/metaphor/.gitignore deleted file mode 100644 index fefa4b9c6..000000000 --- a/metaphor/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/node_modules - -/dist \ No newline at end of file diff --git a/metaphor/.gitlab-ci.yml b/metaphor/.gitlab-ci.yml deleted file mode 100644 index b1eaaac3d..000000000 --- a/metaphor/.gitlab-ci.yml +++ /dev/null @@ -1,122 +0,0 @@ -variables: - ARGO_SERVER: 'argo.:443' - ARGO_HTTP1: 'true' - ARGO_SECURE: 'true' - ARGO_NAMESPACE: argo - DOCKER_TLS_CERTDIR: "/certs" - DOCKER_TLS_VERIFY: 1 - DOCKER_CERT_PATH: "${DOCKER_TLS_CERTDIR}/client" - BUILDER_IMAGE: "kubefirst/chubbo:0.2" - -stages: - - branch - - publish - - development - - staging - - release - -lint: - image: "${BUILDER_IMAGE}" - stage: branch - only: - - branches - except: - - main - script: - - | - argo submit .argo/npm-run.yaml \ - --generate-name="${CI_PROJECT_NAME}-lint-${CI_COMMIT_SHORT_SHA}" \ - -p appName="${CI_PROJECT_NAME}" \ - -p branch="${CI_COMMIT_REF_NAME}" \ - -p gitRepoUrl="${CI_PROJECT_URL}" \ - -p npmScriptName="lint" \ - --wait --log - -build: - image: "${BUILDER_IMAGE}" - stage: branch - only: - - branches - except: - - main - script: - - | - argo submit .argo/npm-run.yaml \ - --generate-name="${CI_PROJECT_NAME}-build-${CI_COMMIT_SHORT_SHA}" \ - -p appName="${CI_PROJECT_NAME}" \ - -p branch="${CI_COMMIT_REF_NAME}" \ - -p gitRepoUrl="${CI_PROJECT_URL}" \ - -p npmScriptName="build" \ - --wait --log - -publish: - image: "${BUILDER_IMAGE}" - stage: publish - only: - - main - script: - - echo "commit sha $CI_COMMIT_SHA" - - | - argo submit .argo/publish.yaml \ - --generate-name="${CI_PROJECT_NAME}-publish-${CI_COMMIT_SHORT_SHA}" \ - -p appName="${CI_PROJECT_NAME}" \ - -p ciCommitSha="${CI_COMMIT_SHA}" \ - -p branch="${CI_COMMIT_REF_NAME}" \ - -p gitRepoUrl="${CI_PROJECT_URL}" \ - -p shortSha="${CI_COMMIT_SHORT_SHA}" \ - --wait --log - - -development: - image: "${BUILDER_IMAGE}" - stage: development - only: - - main - script: - - echo "commit sha $CI_COMMIT_SHA" - - | - argo submit .argo/deploy.yaml \ - --generate-name="${CI_PROJECT_NAME}-development-${CI_COMMIT_SHORT_SHA}" \ - -p appName="${CI_PROJECT_NAME}" \ - -p ciCommitSha="${CI_COMMIT_SHA}" \ - -p branch="${CI_COMMIT_REF_NAME}" \ - -p environment="development" \ - -p gitRepoUrl="${CI_PROJECT_URL}" \ - -p shortSha="${CI_COMMIT_SHORT_SHA}" \ - --wait --log - -staging: - image: "${BUILDER_IMAGE}" - stage: staging - only: - - main - script: - - echo "commit sha $CI_COMMIT_SHA" - - | - argo submit .argo/deploy.yaml \ - --generate-name="${CI_PROJECT_NAME}-staging-${CI_COMMIT_SHORT_SHA}" \ - -p appName="${CI_PROJECT_NAME}" \ - -p ciCommitSha="${CI_COMMIT_SHA}" \ - -p branch="${CI_COMMIT_REF_NAME}" \ - -p environment="staging" \ - -p gitRepoUrl="${CI_PROJECT_URL}" \ - -p shortSha="${CI_COMMIT_SHORT_SHA}" \ - --wait --log - -release: - image: "${BUILDER_IMAGE}" - stage: release - only: - - main - script: - - echo "commit sha $CI_COMMIT_SHA" - - | - argo submit .argo/release.yaml \ - --generate-name="${CI_PROJECT_NAME}-release-${CI_COMMIT_SHORT_SHA}" \ - -p appName="${CI_PROJECT_NAME}" \ - -p ciCommitSha="${CI_COMMIT_SHA}" \ - -p branch="${CI_COMMIT_REF_NAME}" \ - -p environment="production" \ - -p gitRepoUrl="${CI_PROJECT_URL}" \ - -p shortSha="${CI_COMMIT_SHORT_SHA}" \ - --wait --log diff --git a/metaphor/Dockerfile b/metaphor/Dockerfile deleted file mode 100644 index 8d317fa57..000000000 --- a/metaphor/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM node:12.18.4 AS build -WORKDIR /app -ADD package*.json ./ -RUN npm ci -ADD ./ . -ENV PORT=3000 -EXPOSE $PORT -RUN npm run build - - -FROM node:12.18.4-alpine3.12 AS production -ENV PORT=3000 -ENV DOTENV_CONFIG_PATH="/vault/secrets/.env" -EXPOSE $PORT - -USER node -WORKDIR /app - -COPY --from=build --chown=node:node /app/dist /app/dist -COPY --from=build --chown=node:node /app/views /app/dist/views -COPY --from=build --chown=node:node /app/node_modules /app/node_modules -# add any new deployable directories and files from the build stage here - -CMD ["node", "dist/server.js"] diff --git a/metaphor/LICENSE b/metaphor/LICENSE deleted file mode 100644 index f16dd0b50..000000000 --- a/metaphor/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 kubefirst - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/metaphor/README.md b/metaphor/README.md deleted file mode 100644 index 50fbf29be..000000000 --- a/metaphor/README.md +++ /dev/null @@ -1,36 +0,0 @@ -![](logo.png) - -`metaphor` is an example application which serves as a demonstration space for how your applications hook into your infrastructure and tooling. - -`metaphor` is deployed to all of your environments just like your other applications will be. This means that when you make changes to your ci/cd, you can test it out using an application that works just like your applications do. - -The deployed instances of `metaphor` are available at: - -- https://metaphor-development. -- https://metaphor-staging. -- https://metaphor-production. - -`metaphor` currently demonstrates the following capabilities: - -- building a docker container -- publishing a docker container to ecr -- publishing a prerelease helm chart -- gitops delivery of metaphor to different namespaces -- a release process that publishes a release chart and patches the chart version to prepare for the next release -- secrets sourced from vault -- certificate management using cert-manager -- automatic dns management using external-dns - -# CI/CD - -`metaphor` has 5 ci stages defined in its .gitlab-ci.yml file. - - - branch: branch jobs run when your branch pushes to origin and report status to your merge requests - - publish: publishes your docker container to ecr and publishes your prerelease chart to chartmuseum - - development: sets the desired chart version for development - - staging: sets the desired chart version for staging - - release: publishes a release chart, sets the desired chart version for production, and patches chart in source for the next release - -`argocd` is the gitops tool responsible for autosyncing the desired state in each environment. It follows a pull model so our CI/CD ecosystem doesn't need to know how to connect to our kubernetes clusters. - -We have `metaphor` set up to run its automation by invoking argo-workflows. Those submitted workflows can be found in the .argo directory of this repository. diff --git a/metaphor/charts/metaphor/.helmignore b/metaphor/charts/metaphor/.helmignore deleted file mode 100644 index 50af03172..000000000 --- a/metaphor/charts/metaphor/.helmignore +++ /dev/null @@ -1,22 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/metaphor/charts/metaphor/Chart.yaml b/metaphor/charts/metaphor/Chart.yaml deleted file mode 100644 index 61b973342..000000000 --- a/metaphor/charts/metaphor/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -appVersion: 8c5fd0ea4e0523c953b317fc5fe9fa89dac9688f -description: A Helm chart for Kubernetes -name: metaphor -type: application -version: 0.1.0 diff --git a/metaphor/charts/metaphor/templates/NOTES.txt b/metaphor/charts/metaphor/templates/NOTES.txt deleted file mode 100644 index 5c3d67143..000000000 --- a/metaphor/charts/metaphor/templates/NOTES.txt +++ /dev/null @@ -1,21 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "metaphor.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "metaphor.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "metaphor.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "metaphor.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/metaphor/charts/metaphor/templates/_helpers.tpl b/metaphor/charts/metaphor/templates/_helpers.tpl deleted file mode 100644 index 4fcd1e0d9..000000000 --- a/metaphor/charts/metaphor/templates/_helpers.tpl +++ /dev/null @@ -1,63 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "metaphor.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "metaphor.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "metaphor.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "metaphor.labels" -}} -helm.sh/chart: {{ include "metaphor.chart" . }} -{{ include "metaphor.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define "metaphor.selectorLabels" -}} -app.kubernetes.io/name: {{ include "metaphor.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "metaphor.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "metaphor.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} diff --git a/metaphor/charts/metaphor/templates/cm.yaml b/metaphor/charts/metaphor/templates/cm.yaml deleted file mode 100644 index 5a45a4988..000000000 --- a/metaphor/charts/metaphor/templates/cm.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "metaphor.fullname" . }} - labels: - chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -data: - CONFIG_ONE: your-first-config - CONFIG_TWO: your-second-config diff --git a/metaphor/charts/metaphor/templates/deployment.yaml b/metaphor/charts/metaphor/templates/deployment.yaml deleted file mode 100644 index 009c50cfe..000000000 --- a/metaphor/charts/metaphor/templates/deployment.yaml +++ /dev/null @@ -1,75 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "metaphor.fullname" . }} - labels: - {{- include "metaphor.labels" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - {{- include "metaphor.selectorLabels" . | nindent 6 }} - template: - metadata: - labels: - {{- include "metaphor.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "metaphor.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - envFrom: - - configMapRef: - name: {{ template "metaphor.fullname" . }} - - secretRef: - name: {{ template "metaphor.fullname" . }} - env: - - name: CHART_VERSION - value: "{{ .Chart.Version }}" - - name: DOCKER_TAG - value: "{{ .Chart.AppVersion }}" - ports: - - name: http - containerPort: 3000 - protocol: TCP - livenessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 10 - periodSeconds: 5 - successThreshold: 1 - failureThreshold: 1 - timeoutSeconds: 30 - readinessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 10 - periodSeconds: 5 - successThreshold: 1 - failureThreshold: 3 - timeoutSeconds: 1 - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/metaphor/charts/metaphor/templates/external-secrets.yaml b/metaphor/charts/metaphor/templates/external-secrets.yaml deleted file mode 100644 index 8decd141f..000000000 --- a/metaphor/charts/metaphor/templates/external-secrets.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: "external-secrets.io/v1alpha1" -kind: ExternalSecret -metadata: - name: {{ template "metaphor.fullname" . }} - labels: - chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - target: - name: {{ template "metaphor.fullname" . }} - secretStoreRef: - kind: ClusterSecretStore - name: vault-secrets-backend - refreshInterval: "10s" - data: - - remoteRef: - key: {{ .Values.vaultSecretPath }} - property: SECRET_ONE - secretKey: SECRET_ONE - - remoteRef: - key: {{ .Values.vaultSecretPath }} - property: SECRET_TWO - secretKey: SECRET_TWO diff --git a/metaphor/charts/metaphor/templates/ingress.yaml b/metaphor/charts/metaphor/templates/ingress.yaml deleted file mode 100644 index 665289364..000000000 --- a/metaphor/charts/metaphor/templates/ingress.yaml +++ /dev/null @@ -1,41 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "metaphor.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "metaphor.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ . }} - backend: - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} -{{- end }} diff --git a/metaphor/charts/metaphor/templates/service.yaml b/metaphor/charts/metaphor/templates/service.yaml deleted file mode 100644 index 4b28e4003..000000000 --- a/metaphor/charts/metaphor/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "metaphor.fullname" . }} - labels: - {{- include "metaphor.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "metaphor.selectorLabels" . | nindent 4 }} diff --git a/metaphor/charts/metaphor/templates/serviceaccount.yaml b/metaphor/charts/metaphor/templates/serviceaccount.yaml deleted file mode 100644 index c25bab477..000000000 --- a/metaphor/charts/metaphor/templates/serviceaccount.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "metaphor.serviceAccountName" . }} - labels: -{{ include "metaphor.labels" . | nindent 4 }} -automountServiceAccountToken: true -{{- end -}} \ No newline at end of file diff --git a/metaphor/charts/metaphor/templates/tests/test-connection.yaml b/metaphor/charts/metaphor/templates/tests/test-connection.yaml deleted file mode 100644 index 20c690b83..000000000 --- a/metaphor/charts/metaphor/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "metaphor.fullname" . }}-test-connection" - labels: -{{ include "metaphor.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test-success -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "metaphor.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/metaphor/charts/metaphor/values.yaml b/metaphor/charts/metaphor/values.yaml deleted file mode 100644 index 5b20c354e..000000000 --- a/metaphor/charts/metaphor/values.yaml +++ /dev/null @@ -1,71 +0,0 @@ -# Default values for metaphor. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: .dkr.ecr..amazonaws.com/metaphor - pullPolicy: IfNotPresent - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: metaphor-sa - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 443 - -ingress: - enabled: true - annotations: - kubernetes.io/ingress.class: nginx - # Change to "letsencrypt-staging" while testing adjustments, change to "letsencrypt-prod" after confirming LE certificate was issued - cert-manager.io/cluster-issuer: "letsencrypt-prod" - hosts: - - host: metaphor-development. - paths: - - / - tls: - - secretName: metaphor-tls - hosts: - - metaphor-development. - - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. -resources: - limits: - cpu: 100m - memory: "128Mi" - requests: - cpu: 40m - memory: "64Mi" - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -vaultMountPoint: kubefirst -vaultSecretPath: development/metaphor diff --git a/metaphor/logo.png b/metaphor/logo.png deleted file mode 100644 index 7cd5d5009f93ddc8c95534aade630c0b4f43f3de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53897 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 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, - "constantinople": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", - "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", - "requires": { - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.1" - } - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "doctypes": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" - }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "error": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", - "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", - "requires": { - "string-template": "~0.2.1", - "xtend": "~4.0.0" - } - }, - "errorhandler": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", - "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", - "requires": { - "accepts": "~1.3.7", - "escape-html": "~1.0.3" - } - }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eslint": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.2.0.tgz", - "integrity": "sha512-erw7XmM+CLxTOickrimJ1SiF55jiNlVSp2qqm0NuBWPtHYQCegD5ZMaW0c3i5ytPqL+SSLaCxdvQXFPLJn+ABw==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.0.4", - "@humanwhocodes/config-array": "^0.6.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^6.0.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.2.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-scope": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", - "integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", - "dev": true - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - }, - "dependencies": { - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - } - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", - "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", - "dev": true, - "requires": { - "acorn": "^8.5.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.0.0" - }, - "dependencies": { - "acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", - "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", - "dev": true - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "global-dirs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", - "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", - "dev": true, - "requires": { - "ini": "^1.3.5" - } - }, - "globals": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", - "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - }, - "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, - "hexer": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/hexer/-/hexer-1.5.0.tgz", - "integrity": "sha1-uGzoCFmOip0YksVx887dhvyfBlM=", - "requires": { - "ansi-color": "^0.2.1", - "minimist": "^1.1.0", - "process": "^0.10.0", - "xtend": "^4.0.0" - } - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", - "requires": { - "has": "^1.0.3" - } - }, - "is-expression": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", - "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", - "requires": { - "acorn": "^7.1.1", - "object-assign": "^4.1.1" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", - "dev": true - }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "jaeger-client": { - "version": "3.18.1", - "resolved": "https://registry.npmjs.org/jaeger-client/-/jaeger-client-3.18.1.tgz", - "integrity": "sha512-eZLM2U6rJvYo0XbzQYFeMYfp29gQix7SKlmDReorp9hJkUwXZtTyxW81AcKdmFCjLHO5tFysTX+394BnjEnUZg==", - "requires": { - "node-int64": "^0.4.0", - "opentracing": "^0.14.4", - "thriftrw": "^3.5.0", - "uuid": "^3.2.1", - "xorshift": "^0.2.0" - } - }, - "js-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", - "requires": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" - } - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "requires": { - "package-json": "^6.3.0" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "long": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz", - "integrity": "sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8=" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" - }, - "nodemon": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.6.tgz", - "integrity": "sha512-4I3YDSKXg6ltYpcnZeHompqac4E6JeAMpGm8tJnB9Y3T0ehasLa4139dJOcCrB93HHrUMsCrKtoAlXTqT5n4AQ==", - "dev": true, - "requires": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.3", - "update-notifier": "^4.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "opentracing": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.5.tgz", - "integrity": "sha512-XLKtEfHxqrWyF1fzxznsv78w3csW41ucHnjiKnfzZLD5FN8UBDZZL1i4q0FR29zjxXhm+2Hop+5Vr/b8tKIvEg==" - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "pg": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/pg/-/pg-7.18.2.tgz", - "integrity": "sha512-Mvt0dGYMwvEADNKy5PMQGlzPudKcKKzJds/VbOeZJpb6f/pI3mmoXX0JksPgI3l3JPP/2Apq7F36O63J7mgveA==", - "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "0.1.3", - "pg-packet-stream": "^1.1.0", - "pg-pool": "^2.0.10", - "pg-types": "^2.1.0", - "pgpass": "1.x", - "semver": "4.3.2" - } - }, - "pg-connection-string": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", - "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=" - }, - "pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" - }, - "pg-packet-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pg-packet-stream/-/pg-packet-stream-1.1.0.tgz", - "integrity": "sha512-kRBH0tDIW/8lfnnOyTwKD23ygJ/kexQVXZs7gEyBljw4FYqimZFxnMMx50ndZ8In77QgfGuItS5LLclC2TtjYg==" - }, - "pg-pool": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.10.tgz", - "integrity": "sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg==" - }, - "pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "requires": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - } - }, - "pgpass": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", - "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", - "requires": { - "split2": "^3.1.1" - } - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" - }, - "postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" - }, - "postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" - }, - "postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "requires": { - "xtend": "^4.0.0" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "process": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/process/-/process-0.10.1.tgz", - "integrity": "sha1-hCRXzFHP7XLcd1r+6vuMYDQ3JyU=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "pug": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", - "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", - "requires": { - "pug-code-gen": "^3.0.2", - "pug-filters": "^4.0.0", - "pug-lexer": "^5.0.1", - "pug-linker": "^4.0.0", - "pug-load": "^3.0.0", - "pug-parser": "^6.0.0", - "pug-runtime": "^3.0.1", - "pug-strip-comments": "^2.0.0" - } - }, - "pug-attrs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", - "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", - "requires": { - "constantinople": "^4.0.1", - "js-stringify": "^1.0.2", - "pug-runtime": "^3.0.0" - } - }, - "pug-code-gen": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", - "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", - "requires": { - "constantinople": "^4.0.1", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.2", - "pug-attrs": "^3.0.0", - "pug-error": "^2.0.0", - "pug-runtime": "^3.0.0", - "void-elements": "^3.1.0", - "with": "^7.0.0" - } - }, - "pug-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", - "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" - }, - "pug-filters": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", - "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", - "requires": { - "constantinople": "^4.0.1", - "jstransformer": "1.0.0", - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0", - "resolve": "^1.15.1" - } - }, - "pug-lexer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", - "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", - "requires": { - "character-parser": "^2.2.0", - "is-expression": "^4.0.0", - "pug-error": "^2.0.0" - } - }, - "pug-linker": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", - "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", - "requires": { - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0" - } - }, - "pug-load": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", - "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", - "requires": { - "object-assign": "^4.1.1", - "pug-walk": "^2.0.0" - } - }, - "pug-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", - "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", - "requires": { - "pug-error": "^2.0.0", - "token-stream": "1.0.0" - } - }, - "pug-runtime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", - "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" - }, - "pug-strip-comments": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", - "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", - "requires": { - "pug-error": "^2.0.0" - } - }, - "pug-walk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", - "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "requires": { - "escape-goat": "^2.0.0" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", - "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "requires": { - "readable-stream": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "thriftrw": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/thriftrw/-/thriftrw-3.12.0.tgz", - "integrity": "sha512-4YZvR4DPEI41n4Opwr4jmrLGG4hndxr7387kzRFIIzxHQjarPusH4lGXrugvgb7TtPrfZVTpZCVe44/xUxowEw==", - "requires": { - "bufrw": "^1.3.0", - "error": "7.0.2", - "long": "^2.4.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "token-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", - "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - } - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", - "dev": true - }, - "undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "dev": true, - "requires": { - "debug": "^2.2.0" - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "dev": true, - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "with": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", - "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", - "requires": { - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", - "assert-never": "^1.2.1", - "babel-walk": "3.0.0-canary-5" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, - "xorshift": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/xorshift/-/xorshift-0.2.1.tgz", - "integrity": "sha1-/NgiZ+k1HBPw+5xzMH8lMx0pxjo=" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } - } -} diff --git a/metaphor/package.json b/metaphor/package.json deleted file mode 100644 index 1fd6c934e..000000000 --- a/metaphor/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "metaphor", - "version": "1.31.0", - "description": "A sample application for learning kubernetes", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "watch": "nodemon", - "build": "tsc", - "start": "npm run watch", - "start:built": "node dist/server.js", - "old-lint": "tsc --noEmit && eslint \"**/*.{js,ts}\" --quiet --fix", - "lint": "tslint -p tsconfig.json", - "lint-fix": "npm run lint -- --fix" - }, - "keywords": [ - "typescript", - "node", - "kubernetes" - ], - "author": "Kubefirst", - "license": "MIT", - "dependencies": { - "body-parser": "^1.19.0", - "compression": "^1.7.4", - "errorhandler": "^1.5.1", - "express": "^4.17.1", - "jaeger-client": "^3.17.1", - "opentracing": "^0.14.4", - "pg": "^7.14.0", - "pug": "^3.0.2", - "tslint": "^6.1.3", - "winston": "^3.3.3" - }, - "devDependencies": { - "@types/body-parser": "^1.17.1", - "@types/compression": "^1.0.1", - "@types/errorhandler": "0.0.32", - "@types/eslint": "^6.1.3", - "@types/express": "^4.17.2", - "@types/node": "^12.12.12", - "@typescript-eslint/eslint-plugin": "^2.8.0", - "@typescript-eslint/parser": "^2.8.0", - "eslint": "^8.2.0", - "nodemon": "^2.0.1", - "ts-node": "^8.5.2", - "typescript": "^3.7.2" - } -} diff --git a/metaphor/src/app.ts b/metaphor/src/app.ts deleted file mode 100644 index 03e24ea19..000000000 --- a/metaphor/src/app.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* tslint:disable */ -import bodyParser from "body-parser"; -import compression from "compression"; // compresses requests -import express from "express"; -import fs from "fs"; -import path from "path"; - -import { logger } from "./utils/logger"; -import * as homeView from "./views/home"; - -// Controllers (route handlers) - -// Create Express server -const app = express(); - -function delay(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -app.set("views", path.join(__dirname, "views")); // this is the folder where we keep our pug files -app.set("view engine", "pug"); // we use the engine pug, mustache or EJS work great too - -// serves up static files from the public folder. Anything in public/ will just be served up as the file it is -app.use(express.static(path.join(__dirname, "public"))); - -// Express configuration -app.set("port", process.env.PORT || 3000); -app.use(compression()); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({ extended: true })); - -/** - * Primary app routes. - */ -app.get("/", function(req, res) { - res.render("index", { - appName: "metaphor", - companyName: "kubefirst", - chartVersion: process.env.CHART_VERSION, - dockerTag: process.env.DOCKER_TAG, - secretOne: process.env.SECRET_ONE, - secretTwo: process.env.SECRET_TWO, - configOne: process.env.CONFIG_ONE, - configTwo: process.env.CONFIG_TWO, - }); -}); - -app.get("/performance", async (req, res) => { - const sleepTime = Math.floor(Math.random() * 2 * 1000); - await delay(sleepTime); - res.send({ hello: "world", sleepTime }); -}); - -app.get("/healthz", homeView.status); -app.get("/kill", homeView.kill); - -export default app; diff --git a/metaphor/src/server.ts b/metaphor/src/server.ts deleted file mode 100644 index 87cb3fb9b..000000000 --- a/metaphor/src/server.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* tslint:disable */ -import errorHandler from "errorhandler"; - -import app from "./app"; -import { logger } from "./utils/logger"; - -/** - * error handler. provides full stack - remove for production - */ -if (process.env.ENVIRONMENT != "production") { - app.use(errorHandler()); -} - -/** - * start express server. - */ -const server = app.listen(app.get("port"), () => { - logger.log( - "info", - `app is running at http://localhost:${app.get("port")} in ${app.get( - "env", - )} mode`, - { foo: "bar" }, - ); - logger.log("info", "Press CTRL-C to stop\n", { foo: "baz" }); -}); - -export default server; diff --git a/metaphor/src/utils/logger.ts b/metaphor/src/utils/logger.ts deleted file mode 100644 index fe0966c9c..000000000 --- a/metaphor/src/utils/logger.ts +++ /dev/null @@ -1,8 +0,0 @@ -import winston from "winston"; - -/** - * create logger supported by dd apm auto injection - */ -export const logger = winston.createLogger({ - transports: [new winston.transports.Console()], -}); diff --git a/metaphor/src/utils/tracer.ts b/metaphor/src/utils/tracer.ts deleted file mode 100644 index 352580433..000000000 --- a/metaphor/src/utils/tracer.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* tslint:disable */ -const { initTracer: initJaegerTracer } = require("jaeger-client"); - -// this is the url to try next, DNS debugging kubernetes -// collectorEndpoint: 'http://jaeger-operator-jaeger-collector.default.svc.cluster.local:14268/api/traces', - -module.exports.initTracer = (serviceName: string) => { - const config = { - serviceName, - sampler: { - type: "const", - param: 1, - }, - reporter: { - collectorEndpoint: - "http://jaeger-operator-jaeger-collector:14268/api/traces", - logSpans: true, - }, - }; - const options = { - logger: { - info(msg: string) { - console.log("INFO ", msg); - }, - error(msg: string) { - console.log("ERROR", msg); - }, - }, - }; - - return initJaegerTracer(config, options); -}; diff --git a/metaphor/src/views/home.ts b/metaphor/src/views/home.ts deleted file mode 100644 index 165041e7f..000000000 --- a/metaphor/src/views/home.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* tslint:disable */ -import { Request, Response } from "express"; - -// const { initTracer } = require("../utils/tracer"); -// const tracer = initTracer("property-service"); - -/** - * GET /healthz - * health check route for kubernetes - */ -export const status = (req: Request, res: Response) => { - res.send({ - status: - "ok", - }); -}; - -/** - * GET /kill - * a route to kill the node app - */ -export const kill = (req: Request, res: Response) => { - setTimeout(() => { - process.exit(1); - }, 3000); -}; diff --git a/metaphor/src/views/index.pug b/metaphor/src/views/index.pug deleted file mode 100644 index 97dbfe695..000000000 --- a/metaphor/src/views/index.pug +++ /dev/null @@ -1,23 +0,0 @@ -doctype html -head - title metaphor - - link(rel='preconnect', href='https://fonts.gstatic.com') - link(rel='stylesheet', href='https://fonts.googleapis.com/css2?family=Coda&display=swap', type='text/css') - style - include style.css -body - h1= companyName - #container.col - h2 app details - p app_name: #{appName} - p chart version: #{chartVersion} - p docker tag: #{dockerTag} - #container.col - h2 values from vault - p SECRET_ONE: #{secretOne} - p SECRET_TWO: #{secretTwo} - #container.col - h2 values from configmap.yaml - p CONFIG_ONE: #{configOne} - p CONFIG_TWO: #{configTwo} \ No newline at end of file diff --git a/metaphor/src/views/style.css b/metaphor/src/views/style.css deleted file mode 100644 index 2469b5afe..000000000 --- a/metaphor/src/views/style.css +++ /dev/null @@ -1,23 +0,0 @@ -html { - box-sizing: border-box; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - background: #303030; - overflow-x: hidden; - font-family: 'Coda', cursive; - font-size: 18px; - color: #FFFFFF; -} -body { - margin: 0; - overflow-x: hidden; - text-align: center; - flex-direction: column; - align-items: center; - display: flex; -} -.col { - border: 2px solid #FFF; - margin: 15px; - width: 50%; -} diff --git a/metaphor/tsconfig.json b/metaphor/tsconfig.json deleted file mode 100644 index 0f5d76c37..000000000 --- a/metaphor/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "target": "es6", - "noImplicitAny": true, - "moduleResolution": "node", - "sourceMap": true, - "outDir": "dist", - "baseUrl": ".", - "paths": { - "*": [ - "node_modules/*", - "src/types/*" - ] - } - }, - "include": [ - "src/**/*" - ] -} \ No newline at end of file diff --git a/metaphor/tslint.json b/metaphor/tslint.json deleted file mode 100644 index 776c01fbc..000000000 --- a/metaphor/tslint.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "extends": [ - "tslint:all" // https://github.com/palantir/tslint/blob/master/src/configs/all.ts - ], - "rules": { - "variable-name": [ false ], - "max-line-length": [ false ], - // IDon't like IInterfaces - "interface-name": [ false ], - // both of these enforce alphabetical order on object/class keys - // i don't think this is necessary - "member-ordering": [ false ], - "object-literal-sort-keys": [ false ], - // we should probably do this anyway - "max-classes-per-file": [ false ], - // otherwise requires comments to have capital letters to start them - "comment-format": [ false ], - // no inferrable types conflicts with public params have a type (when used with defaults) - "no-inferrable-types": [ false ], - "completed-docs": [ false ] , - // allows default string values - "strict-boolean-expressions": false, - // throws too many false positives; need to evaluate - "no-unsafe-any": false, - "prefer-function-over-method": false, - "curly": [true, "ignore-same-line"], - "no-any": false, - // TODO: this is actually probably a good rule but there are a lot of places we need to change so TODO for now - "newline-per-chained-call": false, - "typedef": [ - true, - "call-signature", - // "arrow-call-signature", - "parameter", - "arrow-parameter", - "property-declaration", - // "variable-declaration", - "member-variable-declaration" - // "object-destructuring", - // "array-destructuring" - ], - "no-parameter-reassignment": false, - // just for this debug app - "no-console": false - } -} \ No newline at end of file diff --git a/metaphor/views/docker.png b/metaphor/views/docker.png deleted file mode 100644 index a8446bd78d9e7954a31733caceb84f0953bd6a71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22767 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 diff --git a/metaphor/views/index.pug b/metaphor/views/index.pug deleted file mode 100644 index 97dbfe695..000000000 --- a/metaphor/views/index.pug +++ /dev/null @@ -1,23 +0,0 @@ -doctype html -head - title metaphor - - link(rel='preconnect', href='https://fonts.gstatic.com') - link(rel='stylesheet', href='https://fonts.googleapis.com/css2?family=Coda&display=swap', type='text/css') - style - include style.css -body - h1= companyName - #container.col - h2 app details - p app_name: #{appName} - p chart version: #{chartVersion} - p docker tag: #{dockerTag} - #container.col - h2 values from vault - p SECRET_ONE: #{secretOne} - p SECRET_TWO: #{secretTwo} - #container.col - h2 values from configmap.yaml - p CONFIG_ONE: #{configOne} - p CONFIG_TWO: #{configTwo} \ No newline at end of file diff --git a/metaphor/views/style.css b/metaphor/views/style.css deleted file mode 100644 index 2469b5afe..000000000 --- a/metaphor/views/style.css +++ /dev/null @@ -1,23 +0,0 @@ -html { - box-sizing: border-box; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - background: #303030; - overflow-x: hidden; - font-family: 'Coda', cursive; - font-size: 18px; - color: #FFFFFF; -} -body { - margin: 0; - overflow-x: hidden; - text-align: center; - flex-direction: column; - align-items: center; - display: flex; -} -.col { - border: 2px solid #FFF; - margin: 15px; - width: 50%; -} diff --git a/terraform/main.tf b/terraform/main.tf deleted file mode 100644 index 037e3518a..000000000 --- a/terraform/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource "null_resource" "foo" {} \ No newline at end of file From de30a9369f0a4820dd2e46598ba9116b2c3c3a83 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 30 Jun 2022 13:14:06 +0000 Subject: [PATCH 007/107] update globals Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/globals.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/globals.go b/cmd/globals.go index 6e6d01766..547d0cb1a 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -1,4 +1,4 @@ -package cmd + package cmd import ( "fmt" @@ -9,6 +9,8 @@ import ( var home, kubectlClientPath, kubeconfigPath,localOs,localArchitecture,terraformPath,helmClientPath string var dryrunMode bool +//Should this be loaded from somewhere? +var installerEmail = "kubefirst-bot@kubefirst.com" //setGlobals for all common used properties func setGlobals() { tmphome, err := os.UserHomeDir() From c0faaed656e4ef58aa8cba3d594253b214e06035 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 30 Jun 2022 13:19:38 +0000 Subject: [PATCH 008/107] update security check Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 73b9e5710..f6501e468 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,7 +32,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] + language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support From 643070fd4878a261bae435abbb7a5e16b478960c Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 30 Jun 2022 13:30:27 +0000 Subject: [PATCH 009/107] rebranding cli Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 2 +- cmd/destroy.go | 2 +- cmd/init.go | 2 +- cmd/root.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 77d5172fa..366721c07 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -621,7 +621,7 @@ func awaitGitlab() { } func init() { - nebulousCmd.AddCommand(createCmd) + rootCmd.AddCommand(createCmd) // createCmd.Flags().String("tf-entrypoint", "", "the entrypoint to execute the terraform from") // createCmd.MarkFlagRequired("tf-entrypoint") diff --git a/cmd/destroy.go b/cmd/destroy.go index da56bf482..d2b001c3a 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -109,7 +109,7 @@ to quickly create a Cobra application.`, } func init() { - nebulousCmd.AddCommand(destroyCmd) + rootCmd.AddCommand(destroyCmd) destroyCmd.Flags().Bool("skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") diff --git a/cmd/init.go b/cmd/init.go index b52486f90..912bd9099 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -185,7 +185,7 @@ to quickly create a Cobra application.`, } func init() { - nebulousCmd.AddCommand(initCmd) + rootCmd.AddCommand(initCmd) initCmd.Flags().String("hosted-zone-name", "", "the domain to provision the kubefirst platofrm in") initCmd.MarkFlagRequired("hosted-zone-name") diff --git a/cmd/root.go b/cmd/root.go index f0a09f4bd..d514ea5d0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,7 +16,7 @@ var cfgFile string // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ - Use: "flare", + Use: "kubefirst", Short: "A brief description of your application", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your application. For example: From 6906657b06fa1afa757c442cebf8885496df4bf2 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 30 Jun 2022 19:16:14 +0000 Subject: [PATCH 010/107] Add check tools and update outputs Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/checktools.go | 54 +++++++++++++++++++++++++++++++++ cmd/info.go | 23 +++++++------- pkg/flare/envvars.go | 24 ++++++++++++--- pkg/flare/flareFile.go | 16 ++++++++-- pkg/flare/kubefirstDirectory.go | 16 ++++++++-- 5 files changed, 112 insertions(+), 21 deletions(-) create mode 100644 cmd/checktools.go diff --git a/cmd/checktools.go b/cmd/checktools.go new file mode 100644 index 000000000..2944ef91e --- /dev/null +++ b/cmd/checktools.go @@ -0,0 +1,54 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "fmt" + "bytes" + "os/exec" + "github.com/spf13/cobra" +) + +// checktoolsCmd represents the checktools command +var checktoolsCmd = &cobra.Command{ + Use: "checktools", + Short: "use to check compatibility of .kubefirst/tools", + Long: `Execute a compatibility check of the tools downloaded by the installer. + Execute After callint "init". If executed before init, tools will not be available. + `, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Checking the tools installed used by installer:") + + kubectlVersion, kubectlStdErr,errKubectl := execShellReturnStrings(kubectlClientPath, "version", "--client", "--short") + fmt.Printf("-> kubectl version:\n\t%s\n\t%s\n",kubectlVersion,kubectlStdErr) + terraformVersion, terraformStdErr,errTerraform := execShellReturnStrings(terraformPath, "version") + fmt.Printf("-> terraform version:\n\t%s\n\t%s\n",terraformVersion,terraformStdErr) + helmVersion, helmStdErr,errHelm := execShellReturnStrings(helmClientPath, "version", "--client", "--short") + fmt.Printf("-> helm version:\n\t%s\n\t%s\n",helmVersion,helmStdErr) + + if errKubectl != nil { + fmt.Println("failed to call kubectlVersionCmd.Run(): %v", errKubectl) + } + if errHelm != nil { + fmt.Println("failed to call helmVersionCmd.Run(): %v", errHelm) + } + if errTerraform != nil { + fmt.Println("failed to call terraformVersionCmd.Run(): %v", errTerraform) + } + + }, +} + +func init() { + rootCmd.AddCommand(checktoolsCmd) +} +func execShellReturnStrings(command string, args ...string) (string, string, error) { + var outb, errb bytes.Buffer + k := exec.Command(command, args...) + k.Stdout = &outb + k.Stderr = &errb + err := k.Run() + return outb.String(), errb.String(), err +} diff --git a/cmd/info.go b/cmd/info.go index 319b9e78d..4db172863 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -5,7 +5,7 @@ Copyright © 2022 NAME HERE package cmd import ( - "log" + "fmt" "github.com/spf13/cobra" "github.com/kubefirst/nebulous/pkg/flare" ) @@ -18,16 +18,17 @@ var infoCmd = &cobra.Command{ and cli version runnig and its current state. Tool recommended for troubleshooting installations`, Run: func(cmd *cobra.Command, args []string) { - log.Printf("flare-cli golang utility version: v%s", NebolousVersion) - log.Printf("OS type: %s", localOs) - log.Printf("Arch: %s", localArchitecture) - log.Printf("$HOME folder: %s", home) - log.Printf("kubectl used: %s", kubectlClientPath) - log.Printf("terraform used: %s", terraformPath) - log.Printf("Kubeconfig in use: %s", kubeconfigPath) - flare.CheckFlareFile(home) - flare.CheckKubefirstDir(home) - flare.CheckEnvironment() + fmt.Printf("flare-cli golang utility version: v%s \n", NebolousVersion) + fmt.Printf("OS type: %s\n", localOs) + fmt.Printf("Arch: %s\n", localArchitecture) + fmt.Printf("$HOME folder: %s\n", home) + fmt.Printf("kubectl used: %s\n", kubectlClientPath) + fmt.Printf("terraform used: %s\n", terraformPath) + fmt.Printf("Kubeconfig in use: %s\n", kubeconfigPath) + flare.CheckFlareFile(home,true) + flare.CheckKubefirstDir(home,true) + flare.CheckEnvironment(true) + fmt.Printf("----------- \n") }, } diff --git a/pkg/flare/envvars.go b/pkg/flare/envvars.go index 6507910d5..d8f0dd5d5 100755 --- a/pkg/flare/envvars.go +++ b/pkg/flare/envvars.go @@ -2,6 +2,7 @@ package flare import ( "log" + "fmt" "os" ) @@ -9,19 +10,34 @@ import ( // // Output: // $PATH/.flare -func CheckEnvironment() bool { +func CheckEnvironment(printOut bool) bool { if value := os.Getenv("AWS_REGION"); value == "" { log.Printf("AWS_REGION env var not set.") - log.Printf("AWS_REGION is recommended for execution.") + log.Printf("AWS_REGION is recommended for execution.") + if printOut { + fmt.Printf("AWS_REGION env var not set.\n") + fmt.Printf("AWS_REGION is recommended for execution.\n") + } } else { - log.Printf("AWS_REGION env var set: %s",value) + log.Printf("AWS_REGION env var set: %s",value) + if printOut { + fmt.Printf("AWS_REGION env var set: %s\n",value) + } } + if value := os.Getenv("AWS_PROFILE"); value == "" { log.Printf("AWS_PROFILE env var not set.") - log.Printf("AWS_PROFILE is recommended for execution.") + log.Printf("AWS_PROFILE is recommended for execution.") + if printOut { + log.Printf("AWS_PROFILE env var not set. \n") + log.Printf("AWS_PROFILE is recommended for execution.\n") + } } else { log.Printf("AWS_PROFILE env var set: %s",value) + if printOut { + log.Printf("AWS_PROFILE env var set: %s\n",value) + } } diff --git a/pkg/flare/flareFile.go b/pkg/flare/flareFile.go index b45054731..2a37e9252 100755 --- a/pkg/flare/flareFile.go +++ b/pkg/flare/flareFile.go @@ -11,19 +11,29 @@ import ( // // Output: // $PATH/.flare -func CheckFlareFile(home string) string { +func CheckFlareFile(home string, printOut bool) string { flareFile := fmt.Sprintf("%s/.flare", home) if _, err := os.Stat(flareFile); err == nil { // path/to/whatever exists - log.Printf("\".flare\" file found: %s", flareFile) + log.Printf("\".flare\" file found: %s", flareFile) + if printOut { + fmt.Printf("\".flare\" file found: %s \n", flareFile) + } } else if errors.Is(err, os.ErrNotExist) { // path/to/whatever does *not* exist log.Printf("\".flare\" file not found: %s", flareFile) - log.Printf(" \".flare\" is needed to guide installation process" ) + log.Printf(" \".flare\" is needed to guide installation process" ) + if printOut { + fmt.Printf("\".flare\" file not found: %s\n", flareFile) + fmt.Printf(" \".flare\" is needed to guide installation process\n" ) + } } else { // Schrodinger: file may or may not exist. See err for details. // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence log.Printf("Unable to check is \".flare\" if file exists" ) + if printOut { + fmt.Printf("Unable to check is \".flare\" if file exists\n" ) + } } return flareFile } \ No newline at end of file diff --git a/pkg/flare/kubefirstDirectory.go b/pkg/flare/kubefirstDirectory.go index 9ed513fa5..7217ca57a 100755 --- a/pkg/flare/kubefirstDirectory.go +++ b/pkg/flare/kubefirstDirectory.go @@ -12,19 +12,29 @@ import ( // // Output: // $PATH/.kubefirst -func CheckKubefirstDir(home string) string { +func CheckKubefirstDir(home string, printOut bool) string { k1sDir := fmt.Sprintf("%s/.kubefirst", home) if _, err := os.Stat(k1sDir); err == nil { // path/to/whatever exists log.Printf("\".kubefirst\" file found: %s", k1sDir) log.Printf(" \".kubefirst\" will be generated by installation process, if exist means a installation may already be executed" ) + if printOut { + fmt.Printf("\".kubefirst\" file found: %s\n", k1sDir) + fmt.Printf(" \".kubefirst\" will be generated by installation process, if exist means a installation may already be executed\n" ) + } } else if errors.Is(err, os.ErrNotExist) { // path/to/whatever does *not* exist - log.Printf("\".kubefirst\" file not found: %s", k1sDir) + log.Printf("\".kubefirst\" file not found: %s", k1sDir) + if printOut { + fmt.Printf("\".kubefirst\" file not found: %s\n", k1sDir) + } } else { // Schrodinger: file may or may not exist. See err for details. // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence - log.Printf("Unable to check is \".kubefirst\" if file exists" ) + log.Printf("Unable to check is \".kubefirst\" if file exists" ) + if printOut { + fmt.Printf("Unable to check is \".kubefirst\" if file exists\n" ) + } } return k1sDir } \ No newline at end of file From ebf480b8965dba455e2b399c51f877c6efc9e2b2 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 30 Jun 2022 20:15:48 +0000 Subject: [PATCH 011/107] simplify os.shell calls Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/init.go | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 912bd9099..64dac8ca5 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -745,13 +745,12 @@ func download() { os.Setenv("KUBECONFIG", kubeconfigPath) log.Println("going to print the kubeconfig env in runtime", os.Getenv("KUBECONFIG")) - kubectlVersionCmd := exec.Command(kubectlClientPath, "version", "--client", "--short") - kubectlVersionCmd.Stdout = os.Stdout - kubectlVersionCmd.Stderr = os.Stderr - err = kubectlVersionCmd.Run() - if err != nil { + kubectlStdOut, kubectlStdErr,errKubectl := execShellReturnStrings(kubectlClientPath, "version", "--client", "--short") + log.Printf("-> kubectl version:\n\t%s\n\t%s\n",kubectlStdOut,kubectlStdErr) + if errKubectl != nil { log.Println("failed to call kubectlVersionCmd.Run(): %v", err) } + Trackers[trackerStage5].Tracker.Increment(int64(1)) // argocdVersion := "v2.3.4" // argocdDownloadUrl := fmt.Sprintf("https://github.com/argoproj/argo-cd/releases/download/%s/argocd-%s-%s", argocdVersion, localOs, localArchitecture) @@ -795,15 +794,11 @@ func download() { } extractFileFromTarGz(helmTarDownload, fmt.Sprintf("%s-%s/helm", localOs, localArchitecture), helmClientPath) os.Chmod(helmClientPath, 0755) - helmVersionCmd := exec.Command(helmClientPath, "version", "--client", "--short") - + helmStdOut, helmStdErr,errHelm := execShellReturnStrings(helmClientPath, "version", "--client", "--short") + log.Printf("-> kubectl version:\n\t%s\n\t%s\n",helmStdOut,helmStdErr) // currently argocd init values is generated by flare nebulous ssh - // todo helm install argocd --create-namespace --wait --values ~/.kubefirst/argocd-init-values.yaml argo/argo-cd - helmVersionCmd.Stdout = os.Stdout - helmVersionCmd.Stderr = os.Stderr - err = helmVersionCmd.Run() - if err != nil { + if errHelm != nil { log.Println("failed to call helmVersionCmd.Run(): %v", err) } Trackers[trackerStage5].Tracker.Increment(int64(1)) @@ -933,13 +928,11 @@ func createSoftServe(kubeconfigPath string) { // create soft-serve stateful set softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", home) - kubectlCreateSoftServeCmd := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") - kubectlCreateSoftServeCmd.Stdout = os.Stdout - kubectlCreateSoftServeCmd.Stderr = os.Stderr - err = kubectlCreateSoftServeCmd.Run() - if err != nil { + softServeApplyOut, softServeApplyErr,errSoftServeApply := execShellReturnStrings(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") + log.Printf("Result:\n\t%s\n\t%s\n",softServeApplyOut,softServeApplyErr) + if errSoftServeApply != nil { log.Println("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) - } + } } func helmInstallArgocd(home string, kubeconfigPath string) { @@ -965,11 +958,9 @@ func helmInstallArgocd(home string, kubeconfigPath string) { log.Println("failed to call helmRepoUpdate.Run(): %v", err) } - helmInstallArgocdCmd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") - helmInstallArgocdCmd.Stdout = os.Stdout - helmInstallArgocdCmd.Stderr = os.Stderr - err = helmInstallArgocdCmd.Run() - if err != nil { + helmInstallArgocdOut, helmInstallArgocdErr,errHelmInstallArgocd := execShellReturnStrings(helmClientPath, "--kubeconfig", kubeconfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") + log.Printf("Result:\n\t%s\n\t%s\n",helmInstallArgocdOut,helmInstallArgocdErr) + if errHelmInstallArgocd != nil { log.Println("failed to call helmInstallArgocdCmd.Run(): %v", err) } From 3a0d9bf746654f2b1fdf5a39d12125789ca34682 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Fri, 1 Jul 2022 11:50:42 +0000 Subject: [PATCH 012/107] minor notes tweaks Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/README.md | 12 ++++++++++++ cmd/version.go | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd/README.md b/cmd/README.md index ccaac1ea8..219d5a4bc 100644 --- a/cmd/README.md +++ b/cmd/README.md @@ -32,3 +32,15 @@ General overview of the code, to help shuffling parts around. |cfgFile|String| .flare config file| |NebolousVersion|String|CLI version| + +# Commands Available + +| Command | Short Description | Long Description| +|:---|:---|:---| +|checktools|Present, needs review.|Present, needs review.| +|clean|Missing Text|Missing Text| +|create|Missing Text|Missing Text| +|destroy|Missing Text|Missing Text| +|info|Present, needs review.|Present, needs review.| +|init|Missing Text|Missing Text| +|version|Present, needs review.|Present, needs review.| \ No newline at end of file diff --git a/cmd/version.go b/cmd/version.go index b700bc60f..fac8a3338 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -15,8 +15,8 @@ func init() { var versionCmd = &cobra.Command{ Use: "version", - Short: "Print the version number for flare", - Long: `All software has versions. This is flare's`, + Short: "Print the version number for kubefirst-cli", + Long: `All software has versions. This is kubefirst's`, Run: func(cmd *cobra.Command, args []string) { log.Printf("flare-cli golang utility version: v%s", NebolousVersion) From ef7b21ccdb2344635b68aa6206cfd3892e0303b3 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Fri, 1 Jul 2022 12:34:08 +0000 Subject: [PATCH 013/107] cleaning create steps Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/checktools.go | 2 ++ cmd/create.go | 63 +++------------------------------------- cmd/init.go | 34 +++++++++++++++------- cmd/installationSteps.go | 50 +++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 70 deletions(-) create mode 100644 cmd/installationSteps.go diff --git a/cmd/checktools.go b/cmd/checktools.go index 2944ef91e..88f5083bf 100644 --- a/cmd/checktools.go +++ b/cmd/checktools.go @@ -6,6 +6,7 @@ package cmd import ( "fmt" + "log" "bytes" "os/exec" "github.com/spf13/cobra" @@ -50,5 +51,6 @@ func execShellReturnStrings(command string, args ...string) (string, string, err k.Stdout = &outb k.Stderr = &errb err := k.Run() + log.Println("Error executing command: %v", err) return outb.String(), errb.String(), err } diff --git a/cmd/create.go b/cmd/create.go index 366721c07..7b22f331e 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -58,68 +58,13 @@ to quickly create a Cobra application.`, directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) - applyBase := viper.GetBool("create.terraformapplied.base") - createSoftServeFlag := viper.GetBool("create.softserve.create") + + configureAndPushFlag := viper.GetBool("create.softserve.configure") - if applyBase != true { + applyBaseTerraform(cmd,directory) - terraformAction := "apply" - - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) - - err := os.Chdir(directory) - if err != nil { - fmt.Println("error changing dir") - } - - viperDestoryFlag := viper.GetBool("terraform.destroy") - cmdDestroyFlag, _ := cmd.Flags().GetBool("destroy") - - if viperDestoryFlag == true || cmdDestroyFlag == true { - terraformAction = "destroy" - } - - fmt.Println("terraform action: ", terraformAction, "destroyFlag: ", viperDestoryFlag) - tfInitCmd := exec.Command(terraformPath, "init") - tfInitCmd.Stdout = os.Stdout - tfInitCmd.Stderr = os.Stderr - err = tfInitCmd.Run() - if err != nil { - fmt.Println("failed to call tfInitCmd.Run(): ", err) - } - tfApplyCmd := exec.Command(terraformPath, fmt.Sprintf("%s", terraformAction), "-auto-approve") - tfApplyCmd.Stdout = os.Stdout - tfApplyCmd.Stderr = os.Stderr - err = tfApplyCmd.Run() - if err != nil { - fmt.Println("failed to call tfApplyCmd.Run(): ", err) - panic("tfApplyCmd.Run() failed") - } - keyIdBytes, err := exec.Command(terraformPath, "output", "vault_unseal_kms_key").Output() - if err != nil { - fmt.Println("failed to call tfOutputCmd.Run(): ", err) - } - keyId := strings.TrimSpace(string(keyIdBytes)) - - fmt.Println("keyid is:", keyId) - viper.Set("vault.kmskeyid", keyId) - viper.Set("create.terraformapplied.base", true) - viper.WriteConfig() - - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) - - } - if createSoftServeFlag != true { - createSoftServe(kubeconfigPath) - viper.Set("create.softserve.create", true) - viper.WriteConfig() - fmt.Println("waiting for soft-serve installation to complete...") - time.Sleep(60 * time.Second) - - } + createSoftServe(kubeconfigPath) if configureAndPushFlag != true { kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") diff --git a/cmd/init.go b/cmd/init.go index 64dac8ca5..51d819b33 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -918,21 +918,33 @@ func extractTarGz(gzipStream io.Reader) { } func createSoftServe(kubeconfigPath string) { + createSoftServeFlag := viper.GetBool("create.softserve.create") + + if createSoftServeFlag != true { + log.Println("Executing createSoftServe") + toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) - toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) + err := os.Mkdir(toolsDir, 0777) + if err != nil { + log.Println("error creating directory %s", toolsDir, err) + } - err := os.Mkdir(toolsDir, 0777) - if err != nil { - log.Println("error creating directory %s", toolsDir, err) + // create soft-serve stateful set + softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", home) + softServeApplyOut, softServeApplyErr,errSoftServeApply := execShellReturnStrings(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") + log.Printf("Result:\n\t%s\n\t%s\n",softServeApplyOut,softServeApplyErr) + if errSoftServeApply != nil { + log.Println("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) + } + + viper.Set("create.softserve.create", true) + viper.WriteConfig() + fmt.Println("waiting for soft-serve installation to complete...") + time.Sleep(60 * time.Second) + } else { + log.Println("Skipping: createSoftServe") } - // create soft-serve stateful set - softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", home) - softServeApplyOut, softServeApplyErr,errSoftServeApply := execShellReturnStrings(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") - log.Printf("Result:\n\t%s\n\t%s\n",softServeApplyOut,softServeApplyErr) - if errSoftServeApply != nil { - log.Println("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) - } } func helmInstallArgocd(home string, kubeconfigPath string) { diff --git a/cmd/installationSteps.go b/cmd/installationSteps.go new file mode 100644 index 000000000..cabbed7a4 --- /dev/null +++ b/cmd/installationSteps.go @@ -0,0 +1,50 @@ +package cmd + +import ( + "fmt" + "log" + "os" + "strings" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func applyBaseTerraform(cmd *cobra.Command,directory string){ + applyBase := viper.GetBool("create.terraformapplied.base") + if applyBase != true { + log.Println("Executing ApplyBaseTerraform") + terraformAction := "apply" + + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) + + err := os.Chdir(directory) + if err != nil { + fmt.Println("error changing dir") + } + + viperDestoryFlag := viper.GetBool("terraform.destroy") + cmdDestroyFlag, _ := cmd.Flags().GetBool("destroy") + + if viperDestoryFlag == true || cmdDestroyFlag == true { + terraformAction = "destroy" + } + + log.Println("terraform action: ", terraformAction, "destroyFlag: ", viperDestoryFlag) + execShellReturnStrings(terraformPath, "init") + execShellReturnStrings(terraformPath, fmt.Sprintf("%s", terraformAction), "-auto-approve") + keyOut, _, errKey := execShellReturnStrings(terraformPath, "output", "vault_unseal_kms_key") + if errKey != nil { + fmt.Println("failed to call tfOutputCmd.Run(): ", err) + } + keyId := strings.TrimSpace(keyOut) + fmt.Println("keyid is:", keyId) + viper.Set("vault.kmskeyid", keyId) + viper.Set("create.terraformapplied.base", true) + viper.WriteConfig() + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) + } else { + log.Println("Skipping: ApplyBaseTerraform") + } +} From a03550f2a77b51c8b18380208dde3638bedaf82b Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:02:05 +0000 Subject: [PATCH 014/107] address zip slip Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/init.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 51d819b33..2133a0b74 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -21,6 +21,7 @@ import ( "strconv" "strings" "time" + "path" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/route53" @@ -894,11 +895,11 @@ func extractTarGz(gzipStream io.Reader) { switch header.Typeflag { case tar.TypeDir: - if err := os.Mkdir(header.Name, 0755); err != nil { + if err := os.Mkdir(path.Clean(header.Name), 0755); err != nil { log.Println("extractTarGz: Mkdir() failed: %s", err.Error()) } case tar.TypeReg: - outFile, err := os.Create(header.Name) + outFile, err := os.Create(path.Clean(header.Name)) if err != nil { log.Println("extractTarGz: Create() failed: %s", err.Error()) } From 0eecf34a3d854b35ab9ec111f4a322f7e6d9bdf1 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:11:31 +0000 Subject: [PATCH 015/107] address zip slip Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/init.go | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 2133a0b74..d14d9ad4c 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -21,7 +21,6 @@ import ( "strconv" "strings" "time" - "path" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/route53" @@ -888,31 +887,33 @@ func extractTarGz(gzipStream io.Reader) { if err == io.EOF { break } - if err != nil { log.Println("extractTarGz: Next() failed: %s", err.Error()) } - - switch header.Typeflag { - case tar.TypeDir: - if err := os.Mkdir(path.Clean(header.Name), 0755); err != nil { - log.Println("extractTarGz: Mkdir() failed: %s", err.Error()) - } - case tar.TypeReg: - outFile, err := os.Create(path.Clean(header.Name)) - if err != nil { - log.Println("extractTarGz: Create() failed: %s", err.Error()) - } - if _, err := io.Copy(outFile, tarReader); err != nil { - log.Println("extractTarGz: Copy() failed: %s", err.Error()) + p, _ := filepath.Abs(header.Name) + if !strings.Contains(p, "..") { + + switch header.Typeflag { + case tar.TypeDir: + if err := os.Mkdir(header.Name, 0755); err != nil { + log.Println("extractTarGz: Mkdir() failed: %s", err.Error()) + } + case tar.TypeReg: + outFile, err := os.Create(header.Name) + if err != nil { + log.Println("extractTarGz: Create() failed: %s", err.Error()) + } + if _, err := io.Copy(outFile, tarReader); err != nil { + log.Println("extractTarGz: Copy() failed: %s", err.Error()) + } + outFile.Close() + + default: + log.Println( + "extractTarGz: uknown type: %s in %s", + header.Typeflag, + header.Name) } - outFile.Close() - - default: - log.Println( - "extractTarGz: uknown type: %s in %s", - header.Typeflag, - header.Name) } } From 47561135bc7780248665211a3c81244081f3adf6 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:56:38 +0000 Subject: [PATCH 016/107] cleaning some steps Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 85 ++++------------------------------------ cmd/installationSteps.go | 83 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 78 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 7b22f331e..a3ef5c290 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -13,11 +13,9 @@ import ( "html/template" "log" "net/http" - "net/url" "os" "os/exec" "strings" - "syscall" "time" b64 "encoding/base64" @@ -58,34 +56,16 @@ to quickly create a Cobra application.`, directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) - - - configureAndPushFlag := viper.GetBool("create.softserve.configure") + applyBaseTerraform(cmd,directory) createSoftServe(kubeconfigPath) - - if configureAndPushFlag != true { - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - fmt.Println("failed to call kPortForward.Run(): ", err) - } - time.Sleep(10 * time.Second) - - configureSoftServe() - pushGitopsToSoftServe() - viper.Set("create.softserve.configure", true) - viper.WriteConfig() - } - - time.Sleep(10 * time.Second) + + configureSoftserveAndPush() helmInstallArgocd(home, kubeconfigPath) + awaitGitlab() fmt.Println("discovering gitlab toolbox pod") @@ -152,60 +132,9 @@ to quickly create a Cobra application.`, fmt.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) } - if !viper.GetBool("create.terraformapplied.gitlab") { - // Prepare for terraform gitlab execution - os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) - os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) - - directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) - err = os.Chdir(directory) - if err != nil { - fmt.Println("error changing dir") - } - - tfInitCmd := exec.Command(terraformPath, "init") - tfInitCmd.Stdout = os.Stdout - tfInitCmd.Stderr = os.Stderr - err = tfInitCmd.Run() - if err != nil { - fmt.Println("failed to call tfInitCmd.Run(): ", err) - } - - tfApplyCmd := exec.Command(terraformPath, "apply", "-auto-approve") - tfApplyCmd.Stdout = os.Stdout - tfApplyCmd.Stderr = os.Stderr - err = tfApplyCmd.Run() - if err != nil { - fmt.Println("failed to call tfApplyCmd.Run(): ", err) - } - - viper.Set("create.terraformapplied.gitlab", true) - viper.WriteConfig() - } - - // upload ssh public key - if !viper.GetBool("gitlab.keyuploaded") { - fmt.Println("uploading ssh public key to gitlab") - data := url.Values{ - "title": {"kubefirst"}, - "key": {viper.GetString("botpublickey")}, - } - - gitlabUrlBase := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) - - resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) - if err != nil { - log.Fatal(err) - } - var res map[string]interface{} - json.NewDecoder(resp.Body).Decode(&res) - fmt.Println(res) - fmt.Println("ssh public key uploaded to gitlab") - viper.Set("gitlab.keyuploaded", true) - viper.WriteConfig() - } else { - fmt.Println("ssh public key already uploaded to gitlab") - } + applyGitlabTerraform(directory) + + gitlabKeyUpload() pushGitopsToGitLab() changeRegistryToGitLab() diff --git a/cmd/installationSteps.go b/cmd/installationSteps.go index cabbed7a4..5f0b238c6 100644 --- a/cmd/installationSteps.go +++ b/cmd/installationSteps.go @@ -7,6 +7,14 @@ import ( "strings" "github.com/spf13/cobra" "github.com/spf13/viper" + + "os/exec" + "syscall" + "time" + + "net/url" + "net/http" + "encoding/json" ) func applyBaseTerraform(cmd *cobra.Command,directory string){ @@ -48,3 +56,78 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ log.Println("Skipping: ApplyBaseTerraform") } } + + +func applyGitlabTerraform(directory string){ + if !viper.GetBool("create.terraformapplied.gitlab") { + log.Println("Executing applyGitlabTerraform") + // Prepare for terraform gitlab execution + os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) + os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) + + directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) + err := os.Chdir(directory) + if err != nil { + fmt.Println("error changing dir") + } + execShellReturnStrings(terraformPath, "init") + execShellReturnStrings(terraformPath, "apply", "-auto-approve") + viper.Set("create.terraformapplied.gitlab", true) + viper.WriteConfig() + } else { + log.Println("Skipping: applyGitlabTerraform") + } +} + +func configureSoftserveAndPush(){ + configureAndPushFlag := viper.GetBool("create.softserve.configure") + if configureAndPushFlag != true { + log.Println("Executing configureSoftserveAndPush") + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + fmt.Println("failed to call kPortForward.Run(): ", err) + } + time.Sleep(10 * time.Second) + + configureSoftServe() + pushGitopsToSoftServe() + viper.Set("create.softserve.configure", true) + viper.WriteConfig() + time.Sleep(10 * time.Second) + } else { + log.Println("Skipping: configureSoftserveAndPush") + } +} + +func gitlabKeyUpload(){ + // upload ssh public key + if !viper.GetBool("gitlab.keyuploaded") { + log.Println("Executing gitlabKeyUpload") + log.Println("uploading ssh public key to gitlab") + gitlabToken := viper.GetString("gitlab.token") + data := url.Values{ + "title": {"kubefirst"}, + "key": {viper.GetString("botpublickey")}, + } + + gitlabUrlBase := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) + + resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) + if err != nil { + log.Fatal(err) + } + var res map[string]interface{} + json.NewDecoder(resp.Body).Decode(&res) + fmt.Println(res) + fmt.Println("ssh public key uploaded to gitlab") + viper.Set("gitlab.keyuploaded", true) + viper.WriteConfig() + } else { + log.Println("Skipping: gitlabKeyUpload") + log.Println("ssh public key already uploaded to gitlab") + } +} \ No newline at end of file From 410f0a39e65a3bc01de4438cc0c2ea211467eb21 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Fri, 1 Jul 2022 18:36:34 +0000 Subject: [PATCH 017/107] reshuffle create steps Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 76 +--------------------------------------- cmd/installationSteps.go | 74 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index a3ef5c290..c34f5690b 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -25,7 +25,6 @@ import ( gitConfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/google/uuid" vault "github.com/hashicorp/vault/api" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -55,87 +54,14 @@ to quickly create a Cobra application.`, flare.SendTelemetry(metricDomain, metricName) directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) - - - applyBaseTerraform(cmd,directory) - createSoftServe(kubeconfigPath) - configureSoftserveAndPush() - helmInstallArgocd(home, kubeconfigPath) - awaitGitlab() - - fmt.Println("discovering gitlab toolbox pod") - - var outb, errb bytes.Buffer - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pod", "-lapp=toolbox", "-o", "jsonpath='{.items[0].metadata.name}'") - k.Stdout = &outb - k.Stderr = &errb - err := k.Run() - if err != nil { - fmt.Println("failed to call k.Run() to get gitlab pod: ", err) - } - gitlabPodName := outb.String() - gitlabPodName = strings.Replace(gitlabPodName, "'", "", -1) - fmt.Println("gitlab pod", gitlabPodName) - - gitlabToken := viper.GetString("gitlab.token") - if gitlabToken == "" { - - fmt.Println("getting gitlab personal access token") - - id := uuid.New() - gitlabToken = id.String()[:20] - - k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err = k.Run() - if err != nil { - fmt.Println("failed to call k.Run() to set gitlab token: ", err) - } - - viper.Set("gitlab.token", gitlabToken) - viper.WriteConfig() - - fmt.Println("gitlabToken", gitlabToken) - } - - gitlabRunnerToken := viper.GetString("gitlab.runnertoken") - if gitlabRunnerToken == "" { - - fmt.Println("getting gitlab runner token") - - var tokenOut, tokenErr bytes.Buffer - k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "secret", "gitlab-gitlab-runner-secret", "-o", "jsonpath='{.data.runner-registration-token}'") - k.Stdout = &tokenOut - k.Stderr = &tokenErr - err = k.Run() - if err != nil { - fmt.Println("failed to call k.Run() to get gitlabRunnerRegistrationToken: ", err) - } - encodedToken := tokenOut.String() - fmt.Println(encodedToken) - encodedToken = strings.Replace(encodedToken, "'", "", -1) - fmt.Println(encodedToken) - gitlabRunnerRegistrationTokenBytes, err := base64.StdEncoding.DecodeString(encodedToken) - gitlabRunnerRegistrationToken := string(gitlabRunnerRegistrationTokenBytes) - fmt.Println(gitlabRunnerRegistrationToken) - if err != nil { - panic(err) - } - viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) - viper.WriteConfig() - fmt.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) - } - + produceGitlabTokens() applyGitlabTerraform(directory) - gitlabKeyUpload() - pushGitopsToGitLab() changeRegistryToGitLab() configureVault() diff --git a/cmd/installationSteps.go b/cmd/installationSteps.go index 5f0b238c6..0824144ce 100644 --- a/cmd/installationSteps.go +++ b/cmd/installationSteps.go @@ -15,6 +15,10 @@ import ( "net/url" "net/http" "encoding/json" + + "github.com/google/uuid" + "bytes" + "encoding/base64" ) func applyBaseTerraform(cmd *cobra.Command,directory string){ @@ -130,4 +134,74 @@ func gitlabKeyUpload(){ log.Println("Skipping: gitlabKeyUpload") log.Println("ssh public key already uploaded to gitlab") } +} + + +func produceGitlabTokens(){ + //TODO: Should this step be skipped if already executed? + + fmt.Println("discovering gitlab toolbox pod") + + var outb, errb bytes.Buffer + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pod", "-lapp=toolbox", "-o", "jsonpath='{.items[0].metadata.name}'") + k.Stdout = &outb + k.Stderr = &errb + err := k.Run() + if err != nil { + fmt.Println("failed to call k.Run() to get gitlab pod: ", err) + } + gitlabPodName := outb.String() + gitlabPodName = strings.Replace(gitlabPodName, "'", "", -1) + fmt.Println("gitlab pod", gitlabPodName) + + gitlabToken := viper.GetString("gitlab.token") + if gitlabToken == "" { + + fmt.Println("getting gitlab personal access token") + + id := uuid.New() + gitlabToken = id.String()[:20] + + k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + fmt.Println("failed to call k.Run() to set gitlab token: ", err) + } + + viper.Set("gitlab.token", gitlabToken) + viper.WriteConfig() + + fmt.Println("gitlabToken", gitlabToken) + } + + gitlabRunnerToken := viper.GetString("gitlab.runnertoken") + if gitlabRunnerToken == "" { + + fmt.Println("getting gitlab runner token") + + var tokenOut, tokenErr bytes.Buffer + k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "secret", "gitlab-gitlab-runner-secret", "-o", "jsonpath='{.data.runner-registration-token}'") + k.Stdout = &tokenOut + k.Stderr = &tokenErr + err = k.Run() + if err != nil { + fmt.Println("failed to call k.Run() to get gitlabRunnerRegistrationToken: ", err) + } + encodedToken := tokenOut.String() + fmt.Println(encodedToken) + encodedToken = strings.Replace(encodedToken, "'", "", -1) + fmt.Println(encodedToken) + gitlabRunnerRegistrationTokenBytes, err := base64.StdEncoding.DecodeString(encodedToken) + gitlabRunnerRegistrationToken := string(gitlabRunnerRegistrationTokenBytes) + fmt.Println(gitlabRunnerRegistrationToken) + if err != nil { + panic(err) + } + viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) + viper.WriteConfig() + fmt.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) + } + } \ No newline at end of file From 438fa3644d92435fd6d9a5fdde6dccc61ce96faf Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Mon, 4 Jul 2022 13:35:40 +0000 Subject: [PATCH 018/107] Redirecting out to logs Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 47 ++++++++++++++++++++-------------------- cmd/destroy.go | 15 +++++++------ cmd/globals.go | 3 ++- cmd/init.go | 6 ++--- cmd/installationSteps.go | 38 ++++++++++++++++---------------- cmd/kubectl.go | 7 +++--- pkg/flare/aws.go | 8 +++---- pkg/flare/telemetry.go | 7 +++--- 8 files changed, 68 insertions(+), 63 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index c34f5690b..7bd408060 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -90,7 +90,7 @@ func hydrateGitlabMetaphorRepo() { // todo make global domainName := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) - fmt.Println("git remote add origin", domainName) + log.Println("git remote add origin", domainName) _, err = metaphorTemplateRepo.CreateRemote(&gitConfig.RemoteConfig{ Name: "gitlab", URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.git", domainName)}, @@ -98,7 +98,7 @@ func hydrateGitlabMetaphorRepo() { w, _ := metaphorTemplateRepo.Worktree() - fmt.Println("Committing new changes...") + log.Println("Committing new changes...") w.Add(".") w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ Author: &object.Signature{ @@ -116,7 +116,7 @@ func hydrateGitlabMetaphorRepo() { }, }) if err != nil { - fmt.Println("error pushing to remote", err) + log.Println("error pushing to remote", err) } } @@ -169,7 +169,7 @@ func changeRegistryToGitLab() { if err := c.Execute(&secrets, creds); err != nil { log.Panic(err) } - fmt.Println(secrets.String()) + log.Println(secrets.String()) ba := []byte(secrets.String()) err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) @@ -200,7 +200,7 @@ func changeRegistryToGitLab() { if err := c.Execute(&repoSecrets, creds); err != nil { log.Panic(err) } - fmt.Println(repoSecrets.String()) + log.Println(repoSecrets.String()) ba = []byte(repoSecrets.String()) err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) @@ -215,7 +215,7 @@ func changeRegistryToGitLab() { k.Stderr = os.Stderr err = k.Run() if err != nil { - fmt.Println("failed to call k.Run() to apply argocd patch to adopt gitlab: ", err) + log.Println("failed to call k.Run() to apply argocd patch to adopt gitlab: ", err) } viper.Set("gitlab.registry", true) @@ -240,7 +240,7 @@ func addGitlabOidcApplications() { cb["vault"] = fmt.Sprintf("https://vault.%s:8250/oidc/callback http://localhost:8250/oidc/callback https://vault.%s/ui/vault/auth/oidc/oidc/callback http://localhost:8200/ui/vault/auth/oidc/oidc/callback", domain, domain) for _, app := range apps { - fmt.Println("checking to see if", app, "oidc application needs to be created in gitlab") + log.Println("checking to see if", app, "oidc application needs to be created in gitlab") appId := viper.GetString(fmt.Sprintf("gitlab.oidc.%s.applicationid", app)) if appId == "" { @@ -268,7 +268,7 @@ func addGitlabOidcApplications() { } } if created { - fmt.Println("created gitlab oidc application with applicationid", createdApp.ApplicationID) + log.Println("created gitlab oidc application with applicationid", createdApp.ApplicationID) viper.Set(fmt.Sprintf("gitlab.oidc.%s.applicationid", app), createdApp.ApplicationID) viper.Set(fmt.Sprintf("gitlab.oidc.%s.secret", app), createdApp.Secret) @@ -289,7 +289,7 @@ func addGitlabOidcApplications() { } func addVaultSecret(secretPath string, secretData map[string]interface{}) { - fmt.Println("vault called") + log.Println("vault called") config := vault.DefaultConfig() @@ -297,7 +297,7 @@ func addVaultSecret(secretPath string, secretData map[string]interface{}) { client, err := vault.NewClient(config) if err != nil { - fmt.Println("unable to initialize Vault client: ", err) + log.Println("unable to initialize Vault client: ", err) } client.SetToken(viper.GetString("vault.token")) @@ -305,9 +305,9 @@ func addVaultSecret(secretPath string, secretData map[string]interface{}) { // Writing a secret _, err = client.Logical().Write(secretPath, secretData) if err != nil { - fmt.Println("unable to write secret: ", err) + log.Println("unable to write secret: ", err) } else { - fmt.Println("secret written successfully.") + log.Println("secret written successfully.") } } @@ -327,32 +327,33 @@ func configureVault() { // ``` // ... obviously keep the sensitive values bound to vars + //TODO replace this command: var outb, errb bytes.Buffer k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "get", "secret", "vault-unseal-keys", "-o", "jsonpath='{.data.cluster-keys\\.json}'") k.Stdout = &outb k.Stderr = &errb err := k.Run() if err != nil { - fmt.Println("failed to call k.Run() to get gitlab pod: ", err) + log.Println("failed to call k.Run() to get gitlab pod: ", err) } vaultKeysEncoded := outb.String() vaultKeysEncoded = strings.Replace(vaultKeysEncoded, "'", "", -1) - fmt.Println("vault keys", vaultKeysEncoded) + log.Println("vault keys", vaultKeysEncoded) vaultKeysBytes, err := base64.StdEncoding.DecodeString(vaultKeysEncoded) - fmt.Println(vaultKeysBytes) + log.Println(vaultKeysBytes) if err != nil { panic(err) } vaultKeys := string(vaultKeysBytes) - fmt.Println(vaultKeys) + log.Println(vaultKeys) var dat map[string]interface{} if err := json.Unmarshal([]byte(vaultKeys), &dat); err != nil { panic(err) } vaultToken := dat["root_token"].(string) - fmt.Println(vaultToken) + log.Println(vaultToken) viper.Set("vault.token", vaultToken) viper.WriteConfig() @@ -374,7 +375,7 @@ func configureVault() { directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", home) err = os.Chdir(directory) if err != nil { - fmt.Println("error changing dir") + log.Println("error changing dir") } tfInitCmd := exec.Command(terraformPath, "init") @@ -382,7 +383,7 @@ func configureVault() { tfInitCmd.Stderr = os.Stderr err = tfInitCmd.Run() if err != nil { - fmt.Println("failed to call vault terraform init: ", err) + log.Println("failed to call vault terraform init: ", err) } tfApplyCmd := exec.Command(terraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") @@ -390,7 +391,7 @@ func configureVault() { tfApplyCmd.Stderr = os.Stderr err = tfApplyCmd.Run() if err != nil { - fmt.Println("failed to call vault terraform apply: ", err) + log.Println("failed to call vault terraform apply: ", err) } viper.Set("create.terraformapplied.vault", true) @@ -400,7 +401,7 @@ func configureVault() { func awaitGitlab() { - fmt.Println("awaitGitlab called") + log.Println("awaitGitlab called") max := 200 for i := 0; i < max; i++ { @@ -410,11 +411,11 @@ func awaitGitlab() { resp, _ := http.Get(fmt.Sprintf("https://gitlab.%s", hostedZoneName)) if resp != nil && resp.StatusCode == 200 { - fmt.Println("gitlab host resolved, 30 second grace period required...") + log.Println("gitlab host resolved, 30 second grace period required...") time.Sleep(time.Second * 30) i = max } else { - fmt.Println("gitlab host not resolved, sleeping 10s") + log.Println("gitlab host not resolved, sleeping 10s") time.Sleep(time.Second * 10) } } diff --git a/cmd/destroy.go b/cmd/destroy.go index d2b001c3a..3bbb2b0a8 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -6,6 +6,7 @@ package cmd import ( "fmt" + "log" "os" "os/exec" @@ -27,7 +28,7 @@ to quickly create a Cobra application.`, // todo this needs to be removed when we are no longer in the starter account os.Setenv("AWS_PROFILE", "starter") - fmt.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") + log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") // kubeconfig := os.Getenv("HOME") + "/.kube/config" // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) // argocdclientset, err := argocdclientset.NewForConfig(config) @@ -51,7 +52,7 @@ to quickly create a Cobra application.`, directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) err := os.Chdir(directory) if err != nil { - fmt.Println("error changing dir: ", directory) + log.Println("error changing dir: ", directory) } os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) @@ -62,7 +63,7 @@ to quickly create a Cobra application.`, tfInitGitlabCmd.Stderr = os.Stderr err = tfInitGitlabCmd.Run() if err != nil { - fmt.Println("failed to call terraform init gitlab: ", err) + log.Println("failed to call terraform init gitlab: ", err) panic("failed to terraform init gitlab") } @@ -71,7 +72,7 @@ to quickly create a Cobra application.`, tfDestroyGitlabCmd.Stderr = os.Stderr err = tfDestroyGitlabCmd.Run() if err != nil { - fmt.Println("failed to call terraform destroy gitlab: ", err) + log.Println("failed to call terraform destroy gitlab: ", err) panic("failed to terraform destroy gitlab") } @@ -83,7 +84,7 @@ to quickly create a Cobra application.`, directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) err = os.Chdir(directory) if err != nil { - fmt.Println("error changing dir: ", directory) + log.Println("error changing dir: ", directory) } tfInitBaseCmd := exec.Command(terraformPath, "init") @@ -91,7 +92,7 @@ to quickly create a Cobra application.`, tfInitBaseCmd.Stderr = os.Stderr err = tfInitBaseCmd.Run() if err != nil { - fmt.Println("failed to call terraform init base: ", err) + log.Println("failed to call terraform init base: ", err) } tfDestroyBaseCmd := exec.Command(terraformPath, "destroy", "-auto-approve") @@ -99,7 +100,7 @@ to quickly create a Cobra application.`, tfDestroyBaseCmd.Stderr = os.Stderr err = tfDestroyBaseCmd.Run() if err != nil { - fmt.Println("failed to call terraform destroy base: ", err) + log.Println("failed to call terraform destroy base: ", err) panic("failed to terraform destroy base") } diff --git a/cmd/globals.go b/cmd/globals.go index 547d0cb1a..628161f48 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -2,6 +2,7 @@ import ( "fmt" + "log" "os" "runtime" ) @@ -16,7 +17,7 @@ func setGlobals() { tmphome, err := os.UserHomeDir() home = tmphome if(err != nil){ - fmt.Printf("Error Defining home - %s", err) + log.Printf("Error Defining home - %s", err) os.Exit(1) } localOs = runtime.GOOS diff --git a/cmd/init.go b/cmd/init.go index d14d9ad4c..04667e30e 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -164,9 +164,9 @@ to quickly create a Cobra application.`, bucketRand() log.Println("bucketRand() complete\n\n") - fmt.Println("calling detokenize() ") + log.Println("calling detokenize() ") detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) - fmt.Println("detokenize() complete\n\n") + log.Println("detokenize() complete\n\n") Trackers[trackerStage8].Tracker.Increment(int64(1)) // modConfigYaml() @@ -941,7 +941,7 @@ func createSoftServe(kubeconfigPath string) { viper.Set("create.softserve.create", true) viper.WriteConfig() - fmt.Println("waiting for soft-serve installation to complete...") + log.Println("waiting for soft-serve installation to complete...") time.Sleep(60 * time.Second) } else { log.Println("Skipping: createSoftServe") diff --git a/cmd/installationSteps.go b/cmd/installationSteps.go index 0824144ce..27adfcd24 100644 --- a/cmd/installationSteps.go +++ b/cmd/installationSteps.go @@ -33,7 +33,7 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ err := os.Chdir(directory) if err != nil { - fmt.Println("error changing dir") + log.Println("error changing dir") } viperDestoryFlag := viper.GetBool("terraform.destroy") @@ -48,10 +48,10 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ execShellReturnStrings(terraformPath, fmt.Sprintf("%s", terraformAction), "-auto-approve") keyOut, _, errKey := execShellReturnStrings(terraformPath, "output", "vault_unseal_kms_key") if errKey != nil { - fmt.Println("failed to call tfOutputCmd.Run(): ", err) + log.Println("failed to call tfOutputCmd.Run(): ", err) } keyId := strings.TrimSpace(keyOut) - fmt.Println("keyid is:", keyId) + log.Println("keyid is:", keyId) viper.Set("vault.kmskeyid", keyId) viper.Set("create.terraformapplied.base", true) viper.WriteConfig() @@ -72,7 +72,7 @@ func applyGitlabTerraform(directory string){ directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) err := os.Chdir(directory) if err != nil { - fmt.Println("error changing dir") + log.Println("error changing dir") } execShellReturnStrings(terraformPath, "init") execShellReturnStrings(terraformPath, "apply", "-auto-approve") @@ -93,7 +93,7 @@ func configureSoftserveAndPush(){ err := kPortForward.Start() defer kPortForward.Process.Signal(syscall.SIGTERM) if err != nil { - fmt.Println("failed to call kPortForward.Run(): ", err) + log.Println("failed to call kPortForward.Run(): ", err) } time.Sleep(10 * time.Second) @@ -126,8 +126,8 @@ func gitlabKeyUpload(){ } var res map[string]interface{} json.NewDecoder(resp.Body).Decode(&res) - fmt.Println(res) - fmt.Println("ssh public key uploaded to gitlab") + log.Println(res) + log.Println("ssh public key uploaded to gitlab") viper.Set("gitlab.keyuploaded", true) viper.WriteConfig() } else { @@ -140,7 +140,7 @@ func gitlabKeyUpload(){ func produceGitlabTokens(){ //TODO: Should this step be skipped if already executed? - fmt.Println("discovering gitlab toolbox pod") + log.Println("discovering gitlab toolbox pod") var outb, errb bytes.Buffer k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pod", "-lapp=toolbox", "-o", "jsonpath='{.items[0].metadata.name}'") @@ -148,16 +148,16 @@ func produceGitlabTokens(){ k.Stderr = &errb err := k.Run() if err != nil { - fmt.Println("failed to call k.Run() to get gitlab pod: ", err) + log.Println("failed to call k.Run() to get gitlab pod: ", err) } gitlabPodName := outb.String() gitlabPodName = strings.Replace(gitlabPodName, "'", "", -1) - fmt.Println("gitlab pod", gitlabPodName) + log.Println("gitlab pod", gitlabPodName) gitlabToken := viper.GetString("gitlab.token") if gitlabToken == "" { - fmt.Println("getting gitlab personal access token") + log.Println("getting gitlab personal access token") id := uuid.New() gitlabToken = id.String()[:20] @@ -167,19 +167,19 @@ func produceGitlabTokens(){ k.Stderr = os.Stderr err = k.Run() if err != nil { - fmt.Println("failed to call k.Run() to set gitlab token: ", err) + log.Println("failed to call k.Run() to set gitlab token: ", err) } viper.Set("gitlab.token", gitlabToken) viper.WriteConfig() - fmt.Println("gitlabToken", gitlabToken) + log.Println("gitlabToken", gitlabToken) } gitlabRunnerToken := viper.GetString("gitlab.runnertoken") if gitlabRunnerToken == "" { - fmt.Println("getting gitlab runner token") + log.Println("getting gitlab runner token") var tokenOut, tokenErr bytes.Buffer k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "secret", "gitlab-gitlab-runner-secret", "-o", "jsonpath='{.data.runner-registration-token}'") @@ -187,21 +187,21 @@ func produceGitlabTokens(){ k.Stderr = &tokenErr err = k.Run() if err != nil { - fmt.Println("failed to call k.Run() to get gitlabRunnerRegistrationToken: ", err) + log.Println("failed to call k.Run() to get gitlabRunnerRegistrationToken: ", err) } encodedToken := tokenOut.String() - fmt.Println(encodedToken) + log.Println(encodedToken) encodedToken = strings.Replace(encodedToken, "'", "", -1) - fmt.Println(encodedToken) + log.Println(encodedToken) gitlabRunnerRegistrationTokenBytes, err := base64.StdEncoding.DecodeString(encodedToken) gitlabRunnerRegistrationToken := string(gitlabRunnerRegistrationTokenBytes) - fmt.Println(gitlabRunnerRegistrationToken) + log.Println(gitlabRunnerRegistrationToken) if err != nil { panic(err) } viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) viper.WriteConfig() - fmt.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) + log.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) } } \ No newline at end of file diff --git a/cmd/kubectl.go b/cmd/kubectl.go index 856e9ef0c..c8e835113 100644 --- a/cmd/kubectl.go +++ b/cmd/kubectl.go @@ -8,6 +8,7 @@ import ( "context" "encoding/json" "fmt" + "log" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" ) @@ -25,7 +26,7 @@ var gitlabPodsClient coreV1Types.PodInterface func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) if err != nil { - fmt.Println(err) + log.Println(err) } gitlabToolboxPodName = pods.Items[0].Name @@ -35,7 +36,7 @@ func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) func getVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { name := "vault-unseal-keys" - fmt.Printf("Reading secret %s\n", name) + log.Printf("Reading secret %s\n", name) secret, err := vaultSecretClient.Get(context.TODO(), name, metaV1.GetOptions{}) if err != nil { @@ -56,7 +57,7 @@ func getVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { func getSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key string) string { secret, err := k8sClient.Get(context.TODO(), secretName, metaV1.GetOptions{}) if err != nil { - fmt.Println(fmt.Sprintf("error getting key: %s from secret: %s", key, secretName), err) + log.Println(fmt.Sprintf("error getting key: %s from secret: %s", key, secretName), err) } return string(secret.Data[key]) } diff --git a/pkg/flare/aws.go b/pkg/flare/aws.go index b70763d04..6d8aeed66 100644 --- a/pkg/flare/aws.go +++ b/pkg/flare/aws.go @@ -2,8 +2,8 @@ package flare import ( "context" - "fmt" + "log" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/aws/aws-sdk-go/aws" @@ -13,7 +13,7 @@ func DescribeCluster() { cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { - fmt.Println("failed to load configuration, error:", err) + log.Println("failed to load configuration, error:", err) } // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration eksClient := eks.NewFromConfig(cfg, func(o *eks.Options) { @@ -24,8 +24,8 @@ func DescribeCluster() { Name: aws.String("kubefirst"), }) if err != nil { - fmt.Println("error describing cluster", err) + log.Println("error describing cluster", err) } // todo base64 encoded data : *cluster.Cluster.CertificateAuthority.Data, - fmt.Println("cluster:", *cluster.Cluster.Arn, *cluster.Cluster.Endpoint) + log.Println("cluster:", *cluster.Cluster.Arn, *cluster.Cluster.Endpoint) } diff --git a/pkg/flare/telemetry.go b/pkg/flare/telemetry.go index c1b0e0b36..ee138f1ed 100644 --- a/pkg/flare/telemetry.go +++ b/pkg/flare/telemetry.go @@ -2,6 +2,7 @@ package flare import ( "fmt" + "log" "io/ioutil" "net/http" "strings" @@ -18,7 +19,7 @@ func SendTelemetry(domain, metricName string) { req, err := http.NewRequest(method, url, payload) if err != nil { - fmt.Println(err) + log.Println(err) } req.Header.Add("Content-Type", "application/json") @@ -27,10 +28,10 @@ func SendTelemetry(domain, metricName string) { res, err := client.Do(req) if err != nil { - fmt.Println("error") + log.Println("error") } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) - fmt.Println(string(body)) + log.Println(string(body)) } From 97b5e70bd336c687713e874509432cd6db0a18e5 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Mon, 4 Jul 2022 14:12:16 +0000 Subject: [PATCH 019/107] add create dry-run Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 45 ++++++++++++++++++++++++++++++++++------ cmd/init.go | 18 +++++++++++++--- cmd/installationSteps.go | 26 +++++++++++++++++++---- 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 7bd408060..fcdabfa60 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -51,7 +51,12 @@ to quickly create a Cobra application.`, metricName := "kubefirst.mgmt_cluster_install.started" metricDomain := viper.GetString("aws.domainname") - flare.SendTelemetry(metricDomain, metricName) + if !dryrunMode { + flare.SendTelemetry(metricDomain, metricName) + } else { + log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) + } + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) applyBaseTerraform(cmd,directory) @@ -68,13 +73,21 @@ to quickly create a Cobra application.`, addGitlabOidcApplications() hydrateGitlabMetaphorRepo() metricName = "kubefirst.mgmt_cluster_install.completed" - - flare.SendTelemetry(metricDomain, metricName) + + if !dryrunMode { + flare.SendTelemetry(metricDomain, metricName) + } else { + log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) + } }, } func hydrateGitlabMetaphorRepo() { - + //TODO: Should this be skipped if already executed? + if dryrunMode { + log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") + return + } metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", home) url := "https://github.com/kubefirst/metaphor-template" @@ -123,6 +136,10 @@ func hydrateGitlabMetaphorRepo() { func changeRegistryToGitLab() { if !viper.GetBool("gitlab.registry") { + if dryrunMode { + log.Printf("[#99] Dry-run mode, changeRegistryToGitLab skipped.") + return + } type ArgocdGitCreds struct { PersonalAccessToken string @@ -220,10 +237,17 @@ func changeRegistryToGitLab() { viper.Set("gitlab.registry", true) viper.WriteConfig() + } else { + log.Println("Skipping: changeRegistryToGitLab") } } func addGitlabOidcApplications() { + //TODO: Should this skipped if already executed. + if dryrunMode { + log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") + return + } domain := viper.GetString("aws.domainname") git, err := gitlab.NewClient( viper.GetString("gitlab.token"), @@ -313,7 +337,10 @@ func addVaultSecret(secretPath string, secretData map[string]interface{}) { func configureVault() { if !viper.GetBool("create.terraformapplied.vault") { - + if dryrunMode { + log.Printf("[#99] Dry-run mode, configureVault skipped.") + return + } // ``` // NOTE: the terraform here produces unnecessary $var.varname vars in the atlantis secret for nonsensitive values // the following atlantis secrets shouldn't have vars in the gitops source code for the atlantis secret, they @@ -396,11 +423,16 @@ func configureVault() { viper.Set("create.terraformapplied.vault", true) viper.WriteConfig() + } else { + log.Println("Skipping: configureVault") } } func awaitGitlab() { - + if dryrunMode { + log.Printf("[#99] Dry-run mode, awaitGitlab skipped.") + return + } log.Println("awaitGitlab called") max := 200 for i := 0; i < max; i++ { @@ -428,5 +460,6 @@ func init() { // createCmd.MarkFlagRequired("tf-entrypoint") // todo make this an optional switch and check for it or viper createCmd.Flags().Bool("destroy", false, "destroy resources") + createCmd.PersistentFlags().BoolVarP(&dryrunMode, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") } diff --git a/cmd/init.go b/cmd/init.go index 04667e30e..b36f41f04 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -583,6 +583,12 @@ func pushGitopsToSoftServe() { } func pushGitopsToGitLab() { + if dryrunMode { + log.Printf("[#99] Dry-run mode, pushGitopsToGitLab skipped.") + return + } + + //TODO: should this step to be skipped if already executed? domain := viper.GetString("aws.domainname") detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) @@ -920,10 +926,13 @@ func extractTarGz(gzipStream io.Reader) { } func createSoftServe(kubeconfigPath string) { - createSoftServeFlag := viper.GetBool("create.softserve.create") - + createSoftServeFlag := viper.GetBool("create.softserve.create") if createSoftServeFlag != true { log.Println("Executing createSoftServe") + if dryrunMode { + log.Printf("[#99] Dry-run mode, createSoftServe skipped.") + return + } toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) err := os.Mkdir(toolsDir, 0777) @@ -950,9 +959,12 @@ func createSoftServe(kubeconfigPath string) { } func helmInstallArgocd(home string, kubeconfigPath string) { - argocdCreated := viper.GetBool("create.argocd.helm") if !argocdCreated { + if dryrunMode { + log.Printf("[#99] Dry-run mode, helmInstallArgocd skipped.") + return + } helmClientPath := fmt.Sprintf("%s/.kubefirst/tools/helm", home) // ! commenting out until a clean execution is necessary // create namespace diff --git a/cmd/installationSteps.go b/cmd/installationSteps.go index 27adfcd24..b7ef55b5c 100644 --- a/cmd/installationSteps.go +++ b/cmd/installationSteps.go @@ -25,6 +25,10 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ applyBase := viper.GetBool("create.terraformapplied.base") if applyBase != true { log.Println("Executing ApplyBaseTerraform") + if dryrunMode { + log.Printf("[#99] Dry-run mode, applyBaseTerraform skipped.") + return + } terraformAction := "apply" os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) @@ -65,6 +69,10 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ func applyGitlabTerraform(directory string){ if !viper.GetBool("create.terraformapplied.gitlab") { log.Println("Executing applyGitlabTerraform") + if dryrunMode { + log.Printf("[#99] Dry-run mode, applyGitlabTerraform skipped.") + return + } // Prepare for terraform gitlab execution os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) @@ -87,6 +95,10 @@ func configureSoftserveAndPush(){ configureAndPushFlag := viper.GetBool("create.softserve.configure") if configureAndPushFlag != true { log.Println("Executing configureSoftserveAndPush") + if dryrunMode { + log.Printf("[#99] Dry-run mode, configureSoftserveAndPush skipped.") + return + } kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr @@ -108,9 +120,13 @@ func configureSoftserveAndPush(){ } func gitlabKeyUpload(){ - // upload ssh public key + // upload ssh public key if !viper.GetBool("gitlab.keyuploaded") { log.Println("Executing gitlabKeyUpload") + if dryrunMode { + log.Printf("[#99] Dry-run mode, gitlabKeyUpload skipped.") + return + } log.Println("uploading ssh public key to gitlab") gitlabToken := viper.GetString("gitlab.token") data := url.Values{ @@ -139,9 +155,11 @@ func gitlabKeyUpload(){ func produceGitlabTokens(){ //TODO: Should this step be skipped if already executed? - - log.Println("discovering gitlab toolbox pod") - + log.Println("discovering gitlab toolbox pod") + if dryrunMode { + log.Printf("[#99] Dry-run mode, produceGitlabTokens skipped.") + return + } var outb, errb bytes.Buffer k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pod", "-lapp=toolbox", "-o", "jsonpath='{.items[0].metadata.name}'") k.Stdout = &outb From d4d0076aa3718ccc05f31ce916d3d4054ed52622 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Mon, 4 Jul 2022 16:11:42 +0000 Subject: [PATCH 020/107] add progress bars Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/cmd/create.go b/cmd/create.go index fcdabfa60..da1d5088e 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -36,6 +36,12 @@ import ( "k8s.io/client-go/tools/clientcmd" ) + +const trackerStage20 = "0 - Apply Base" +const trackerStage21 = "1 - Temporary SCM Install" +const trackerStage22 = "2 - Argo/Final SCM Install" +const trackerStage23 = "3 - Final Setup" + // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", @@ -48,6 +54,15 @@ This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { + flare.SetupProgress(4) + Trackers = make(map[string]*flare.ActionTracker) + Trackers[trackerStage20] = &flare.ActionTracker{flare.CreateTracker(trackerStage20, int64(1))} + Trackers[trackerStage21] = &flare.ActionTracker{flare.CreateTracker(trackerStage21, int64(2))} + Trackers[trackerStage22] = &flare.ActionTracker{flare.CreateTracker(trackerStage22, int64(7))} + Trackers[trackerStage23] = &flare.ActionTracker{flare.CreateTracker(trackerStage23, int64(3))} + + infoCmd.Run(cmd, args) + metricName := "kubefirst.mgmt_cluster_install.started" metricDomain := viper.GetString("aws.domainname") @@ -60,18 +75,31 @@ to quickly create a Cobra application.`, directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) applyBaseTerraform(cmd,directory) + Trackers[trackerStage20].Tracker.Increment(int64(1)) createSoftServe(kubeconfigPath) + Trackers[trackerStage21].Tracker.Increment(int64(1)) configureSoftserveAndPush() + Trackers[trackerStage21].Tracker.Increment(int64(1)) helmInstallArgocd(home, kubeconfigPath) + Trackers[trackerStage22].Tracker.Increment(int64(1)) awaitGitlab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) produceGitlabTokens() + Trackers[trackerStage22].Tracker.Increment(int64(1)) applyGitlabTerraform(directory) + Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlabKeyUpload() + Trackers[trackerStage22].Tracker.Increment(int64(1)) pushGitopsToGitLab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) changeRegistryToGitLab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) configureVault() + Trackers[trackerStage23].Tracker.Increment(int64(1)) addGitlabOidcApplications() + Trackers[trackerStage23].Tracker.Increment(int64(1)) hydrateGitlabMetaphorRepo() + Trackers[trackerStage23].Tracker.Increment(int64(1)) metricName = "kubefirst.mgmt_cluster_install.completed" if !dryrunMode { @@ -79,6 +107,7 @@ to quickly create a Cobra application.`, } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } + time.Sleep(time.Millisecond * 100) }, } From 8b2a84aa37e106d3da65d27d8c43b597dd65ca57 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Mon, 4 Jul 2022 16:26:49 +0000 Subject: [PATCH 021/107] fix pr comments Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/checktools.go | 4 +++- cmd/destroy.go | 8 +++----- cmd/globals.go | 3 +-- cmd/init.go | 1 + cmd/installationSteps.go | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/checktools.go b/cmd/checktools.go index 88f5083bf..97562903c 100644 --- a/cmd/checktools.go +++ b/cmd/checktools.go @@ -51,6 +51,8 @@ func execShellReturnStrings(command string, args ...string) (string, string, err k.Stdout = &outb k.Stderr = &errb err := k.Run() - log.Println("Error executing command: %v", err) + if err != nil { + log.Println("Error executing command: %v", err) + } return outb.String(), errb.String(), err } diff --git a/cmd/destroy.go b/cmd/destroy.go index 3bbb2b0a8..12fee7b86 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -28,7 +28,7 @@ to quickly create a Cobra application.`, // todo this needs to be removed when we are no longer in the starter account os.Setenv("AWS_PROFILE", "starter") - log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") + log.Println("TODO -- need to setup and argocd delete against registry and wait?") // kubeconfig := os.Getenv("HOME") + "/.kube/config" // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) // argocdclientset, err := argocdclientset.NewForConfig(config) @@ -63,8 +63,7 @@ to quickly create a Cobra application.`, tfInitGitlabCmd.Stderr = os.Stderr err = tfInitGitlabCmd.Run() if err != nil { - log.Println("failed to call terraform init gitlab: ", err) - panic("failed to terraform init gitlab") + log.Panicf("failed to call terraform init gitlab: ", err) } tfDestroyGitlabCmd := exec.Command(terraformPath, "destroy", "-auto-approve") @@ -72,8 +71,7 @@ to quickly create a Cobra application.`, tfDestroyGitlabCmd.Stderr = os.Stderr err = tfDestroyGitlabCmd.Run() if err != nil { - log.Println("failed to call terraform destroy gitlab: ", err) - panic("failed to terraform destroy gitlab") + log.Panicf("failed to call terraform destroy gitlab: ", err) } viper.Set("destroy.terraformdestroy.gitlab", true) diff --git a/cmd/globals.go b/cmd/globals.go index 628161f48..d16f5e341 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -17,8 +17,7 @@ func setGlobals() { tmphome, err := os.UserHomeDir() home = tmphome if(err != nil){ - log.Printf("Error Defining home - %s", err) - os.Exit(1) + log.Panicf("Error Defining home - %s", err) } localOs = runtime.GOOS localArchitecture = runtime.GOARCH diff --git a/cmd/init.go b/cmd/init.go index b36f41f04..194644e2e 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -952,6 +952,7 @@ func createSoftServe(kubeconfigPath string) { viper.WriteConfig() log.Println("waiting for soft-serve installation to complete...") time.Sleep(60 * time.Second) + //TODO: Update mechanism of waiting } else { log.Println("Skipping: createSoftServe") } diff --git a/cmd/installationSteps.go b/cmd/installationSteps.go index b7ef55b5c..dc83388a3 100644 --- a/cmd/installationSteps.go +++ b/cmd/installationSteps.go @@ -37,7 +37,7 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ err := os.Chdir(directory) if err != nil { - log.Println("error changing dir") + log.Panicf("error changing dir") } viperDestoryFlag := viper.GetBool("terraform.destroy") @@ -52,7 +52,7 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ execShellReturnStrings(terraformPath, fmt.Sprintf("%s", terraformAction), "-auto-approve") keyOut, _, errKey := execShellReturnStrings(terraformPath, "output", "vault_unseal_kms_key") if errKey != nil { - log.Println("failed to call tfOutputCmd.Run(): ", err) + log.Panicf("failed to call tfOutputCmd.Run(): ", err) } keyId := strings.TrimSpace(keyOut) log.Println("keyid is:", keyId) From f0a63e4222263842bf0d8487abcf5eed8191df10 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Mon, 4 Jul 2022 18:54:48 +0000 Subject: [PATCH 022/107] reorg funcs Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/aws.go | 252 +++++++++ cmd/checktools.go | 14 - cmd/create.go | 386 -------------- cmd/files.go | 250 +++++++++ cmd/init.go | 847 +----------------------------- cmd/installationSteps.go | 225 -------- cmd/kubefirstTemplate.go | 169 ++++++ cmd/{kubectl.go => kubernetes.go} | 0 cmd/shell.go | 19 + cmd/stepsArgo.go | 403 ++++++++++++++ cmd/stepsBaseInstall.go | 54 ++ cmd/stepsMetaphor.go | 64 +++ cmd/stepsSoftServe.go | 136 +++++ cmd/stepsUndefined.go | 36 ++ cmd/stepsVault.go | 202 +++++++ 15 files changed, 1586 insertions(+), 1471 deletions(-) create mode 100644 cmd/aws.go create mode 100644 cmd/files.go delete mode 100644 cmd/installationSteps.go create mode 100644 cmd/kubefirstTemplate.go rename cmd/{kubectl.go => kubernetes.go} (100%) create mode 100644 cmd/shell.go create mode 100644 cmd/stepsArgo.go create mode 100644 cmd/stepsBaseInstall.go create mode 100644 cmd/stepsMetaphor.go create mode 100644 cmd/stepsSoftServe.go create mode 100644 cmd/stepsUndefined.go create mode 100644 cmd/stepsVault.go diff --git a/cmd/aws.go b/cmd/aws.go new file mode 100644 index 000000000..b599d1143 --- /dev/null +++ b/cmd/aws.go @@ -0,0 +1,252 @@ +package cmd + +import ( + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/route53" + "github.com/aws/aws-sdk-go-v2/service/route53/types" + "github.com/aws/aws-sdk-go-v2/service/sts" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/cip8/autoname" + "github.com/spf13/viper" + "log" + "os" + "strings" + "fmt" + "context" + "strconv" + "net" + "time" +) + + +func bucketRand() { + sess, err := session.NewSession(&aws.Config{ + Region: aws.String(viper.GetString("aws.region"))}, + ) + if err != nil { + log.Println("failed to attempt bucket creation ", err.Error()) + os.Exit(1) + } + + s3Client := s3.New(sess) + + randomName := strings.ReplaceAll(autoname.Generate(), "_", "-") + viper.Set("bucket.rand", randomName) + + buckets := strings.Fields("state-store argo-artifacts gitlab-backup chartmuseum") + for _, bucket := range buckets { + bucketExists := viper.GetBool(fmt.Sprintf("bucket.%s.created", bucket)) + if !bucketExists { + bucketName := fmt.Sprintf("k1-%s-%s", bucket, randomName) + + log.Println("creating", bucket, "bucket", bucketName) + + regionName := viper.GetString("aws.region") + log.Println("region is ", regionName) + if !dryrunMode { + if regionName == "us-east-1" { + _, err = s3Client.CreateBucket(&s3.CreateBucketInput{ + Bucket: &bucketName, + }) + } else { + _, err = s3Client.CreateBucket(&s3.CreateBucketInput{ + Bucket: &bucketName, + CreateBucketConfiguration: &s3.CreateBucketConfiguration{ + LocationConstraint: aws.String(regionName), + }, + }) + } + if err != nil { + log.Println("failed to create bucket "+bucketName, err.Error()) + os.Exit(1) + } + } else { + log.Printf("[#99] Dry-run mode, bucket creation skipped: %s", bucketName) + } + viper.Set(fmt.Sprintf("bucket.%s.created", bucket), true) + viper.Set(fmt.Sprintf("bucket.%s.name", bucket), bucketName) + viper.WriteConfig() + } + log.Printf("bucket %s exists", viper.GetString(fmt.Sprintf("bucket.%s.name", bucket))) + Trackers[trackerStage7].Tracker.Increment(int64(1)) + } +} + +func getAccountInfo() { + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + log.Println("failed to load configuration, error:", err) + } + // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration + stsClient := sts.NewFromConfig(cfg) + iamCaller, err := stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{}) + if err != nil { + log.Println("oh no error on call", err) + } + + viper.Set("aws.accountid", *iamCaller.Account) + viper.Set("aws.userarn", *iamCaller.Arn) + viper.WriteConfig() +} + +func testHostedZoneLiveness(hostedZoneName, hostedZoneId string) { + //tracker := progress.Tracker{Message: "testing hosted zone", Total: 25} + + // todo need to create single client and pass it + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + log.Println("failed to load configuration, error:", err) + } + // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration + route53Client := route53.NewFromConfig(cfg) + + // todo when checking to see if hosted zone exists print ns records for user to verity in dns registrar + route53RecordName := fmt.Sprintf("kubefirst-liveness.%s", hostedZoneName) + route53RecordValue := "domain record propagated" + + log.Println("checking to see if record", route53RecordName, "exists") + log.Println("hostedZoneId", hostedZoneId) + log.Println("route53RecordName", route53RecordName) + + recordList, err := route53Client.ListResourceRecordSets(context.TODO(), &route53.ListResourceRecordSetsInput{ + HostedZoneId: aws.String(hostedZoneId), + StartRecordName: aws.String(route53RecordName), + StartRecordType: "TXT", + }) + if err != nil { + log.Println("failed read route53 ", err.Error()) + os.Exit(1) + } + + if len(recordList.ResourceRecordSets) == 0 { + if !dryrunMode { + record, err := route53Client.ChangeResourceRecordSets(context.TODO(), &route53.ChangeResourceRecordSetsInput{ + ChangeBatch: &types.ChangeBatch{ + Changes: []types.Change{ + { + Action: "CREATE", + ResourceRecordSet: &types.ResourceRecordSet{ + Name: aws.String(route53RecordName), + Type: "TXT", + ResourceRecords: []types.ResourceRecord{ + { + Value: aws.String(strconv.Quote(route53RecordValue)), + }, + }, + TTL: aws.Int64(10), + Weight: aws.Int64(100), + SetIdentifier: aws.String("CREATE sanity check for kubefirst installation"), + }, + }, + }, + Comment: aws.String("CREATE sanity check dns record."), + }, + HostedZoneId: aws.String(hostedZoneId), + }) + if err != nil { + log.Println(err) + } + log.Println("record creation status is ", record.ChangeInfo.Status) + } else { + log.Printf("[#99] Dry-run mode, route53 creation/update skipped: %s", route53RecordName) + } + } + count := 0 + // todo need to exit after n number of minutes and tell them to check ns records + // todo this logic sucks + for count <= 25 { + count++ + //tracker.Increment(1) + //log.Println(text.Faint.Sprintf("[INFO] dns test %d of 25", count)) + + log.Println(route53RecordName) + ips, err := net.LookupTXT(route53RecordName) + + log.Println(ips) + + if err != nil { + // tracker.Message = fmt.Sprintln("dns test", count, "of", 25) + fmt.Fprintf(os.Stderr, "Could not get record name %s - waiting 10 seconds and trying again: %v\n", route53RecordName, err) + time.Sleep(10 * time.Second) + } else { + for _, ip := range ips { + // todo check ip against route53RecordValue in some capacity so we can pivot the value for testing + log.Printf("%s. in TXT record value: %s\n", route53RecordName, ip) + //tracker.MarkAsDone() + count = 26 + } + } + if count == 25 { + log.Println("unable to resolve hosted zone dns record. please check your domain registrar") + //tracker.MarkAsErrored() + //pw.Stop() + os.Exit(1) + } + } + // todo delete route53 record + + // recordDelete, err := route53Client.ChangeResourceRecordSets(context.TODO(), &route53.ChangeResourceRecordSetsInput{ + // ChangeBatch: &types.ChangeBatch{ + // Changes: []types.Change{ + // { + // Action: "DELETE", + // ResourceRecordSet: &types.ResourceRecordSet{ + // Name: aws.String(route53RecordName), + // Type: "A", + // ResourceRecords: []types.ResourceRecord{ + // { + // Value: aws.String(route53RecordValue), + // }, + // }, + // TTL: aws.Int64(10), + // Weight: aws.Int64(100), + // SetIdentifier: aws.String("CREATE sanity check for kubefirst installation"), + // }, + // }, + // }, + // Comment: aws.String("CREATE sanity check dns record."), + // }, + // HostedZoneId: aws.String(hostedZoneId), + // }) + // if err != nil { + // fmt.Println("error deleting route 53 record after liveness test") + // } + // fmt.Println("record deletion status is ", *&recordDelete.ChangeInfo.Status) + +} + +func getDNSInfo(hostedZoneName string) string { + + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + log.Println("failed to load configuration, error:", err) + } + // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration + route53Client := route53.NewFromConfig(cfg) + hostedZones, err := route53Client.ListHostedZonesByName(context.TODO(), &route53.ListHostedZonesByNameInput{ + DNSName: &hostedZoneName, + }) + if err != nil { + log.Println("oh no error on call", err) + } + + var zoneId string + + for _, zone := range hostedZones.HostedZones { + if *zone.Name == fmt.Sprintf(`%s%s`, hostedZoneName, ".") { + zoneId = returnHostedZoneId(*zone.Id) + log.Printf(`found entry for user submitted domain %s, using hosted zone id %s`, hostedZoneName, zoneId) + viper.Set("aws.domainname", hostedZoneName) + viper.Set("aws.domainid", zoneId) + viper.WriteConfig() + } + } + return zoneId + +} + +func returnHostedZoneId(rawZoneId string) string { + return strings.Split(rawZoneId, "/")[2] +} \ No newline at end of file diff --git a/cmd/checktools.go b/cmd/checktools.go index 97562903c..29ba8011c 100644 --- a/cmd/checktools.go +++ b/cmd/checktools.go @@ -6,9 +6,6 @@ package cmd import ( "fmt" - "log" - "bytes" - "os/exec" "github.com/spf13/cobra" ) @@ -45,14 +42,3 @@ var checktoolsCmd = &cobra.Command{ func init() { rootCmd.AddCommand(checktoolsCmd) } -func execShellReturnStrings(command string, args ...string) (string, string, error) { - var outb, errb bytes.Buffer - k := exec.Command(command, args...) - k.Stdout = &outb - k.Stderr = &errb - err := k.Run() - if err != nil { - log.Println("Error executing command: %v", err) - } - return outb.String(), errb.String(), err -} diff --git a/cmd/create.go b/cmd/create.go index da1d5088e..52f822fd0 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -5,35 +5,12 @@ Copyright © 2022 NAME HERE package cmd import ( - "bytes" - "context" - "encoding/base64" - "encoding/json" "fmt" - "html/template" "log" - "net/http" - "os" - "os/exec" - "strings" "time" - - b64 "encoding/base64" - - "github.com/ghodss/yaml" - "github.com/go-git/go-git/v5" - gitConfig "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing/object" - gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" - vault "github.com/hashicorp/vault/api" "github.com/spf13/cobra" "github.com/spf13/viper" - gitlab "github.com/xanzy/go-gitlab" "github.com/kubefirst/nebulous/pkg/flare" - v1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" ) @@ -111,376 +88,13 @@ to quickly create a Cobra application.`, }, } -func hydrateGitlabMetaphorRepo() { - //TODO: Should this be skipped if already executed? - if dryrunMode { - log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") - return - } - metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", home) - - url := "https://github.com/kubefirst/metaphor-template" - - metaphorTemplateRepo, err := git.PlainClone(metaphorTemplateDir, false, &git.CloneOptions{ - URL: url, - }) - if err != nil { - panic("error cloning metaphor-template repo") - } - - detokenize(metaphorTemplateDir) - - // todo make global - domainName := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) - log.Println("git remote add origin", domainName) - _, err = metaphorTemplateRepo.CreateRemote(&gitConfig.RemoteConfig{ - Name: "gitlab", - URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.git", domainName)}, - }) - - w, _ := metaphorTemplateRepo.Worktree() - - log.Println("Committing new changes...") - w.Add(".") - w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ - Author: &object.Signature{ - Name: "kubefirst-bot", - Email: installerEmail, - When: time.Now(), - }, - }) - - err = metaphorTemplateRepo.Push(&git.PushOptions{ - RemoteName: "gitlab", - Auth: &gitHttp.BasicAuth{ - Username: "root", - Password: viper.GetString("gitlab.token"), - }, - }) - if err != nil { - log.Println("error pushing to remote", err) - } - -} - -func changeRegistryToGitLab() { - if !viper.GetBool("gitlab.registry") { - if dryrunMode { - log.Printf("[#99] Dry-run mode, changeRegistryToGitLab skipped.") - return - } - type ArgocdGitCreds struct { - PersonalAccessToken string - URL string - FullURL string - } - - pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) - url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.domainname")))) - fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.domainname")))) - - creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} - - var argocdRepositoryAccessTokenSecret *v1.Secret - kubeconfig := home + "/.kubefirst/gitops/terraform/base/kubeconfig_kubefirst" - config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) - if err != nil { - panic(err.Error()) - } - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - panic(err.Error()) - } - argocdSecretClient = clientset.CoreV1().Secrets("argocd") - - var secrets bytes.Buffer - - c, err := template.New("creds-gitlab").Parse(` - apiVersion: v1 - data: - password: {{ .PersonalAccessToken }} - url: {{ .URL }} - username: cm9vdA== - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repo-creds - name: creds-gitlab - namespace: argocd - type: Opaque - `) - if err := c.Execute(&secrets, creds); err != nil { - log.Panic(err) - } - log.Println(secrets.String()) - - ba := []byte(secrets.String()) - err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - - _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) - if err != nil { - panic(err) - } - - var repoSecrets bytes.Buffer - - c, err = template.New("repo-gitlab").Parse(` - apiVersion: v1 - data: - project: ZGVmYXVsdA== - type: Z2l0 - url: {{ .FullURL }} - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repository - name: repo-gitlab - namespace: argocd - type: Opaque - `) - if err := c.Execute(&repoSecrets, creds); err != nil { - log.Panic(err) - } - log.Println(repoSecrets.String()) - - ba = []byte(repoSecrets.String()) - err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - - _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) - if err != nil { - panic(err) - } - - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", home)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err = k.Run() - if err != nil { - log.Println("failed to call k.Run() to apply argocd patch to adopt gitlab: ", err) - } - - viper.Set("gitlab.registry", true) - viper.WriteConfig() - } else { - log.Println("Skipping: changeRegistryToGitLab") - } -} -func addGitlabOidcApplications() { - //TODO: Should this skipped if already executed. - if dryrunMode { - log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") - return - } - domain := viper.GetString("aws.domainname") - git, err := gitlab.NewClient( - viper.GetString("gitlab.token"), - gitlab.WithBaseURL(fmt.Sprintf("https://gitlab.%s/api/v4", domain)), - ) - if err != nil { - log.Fatal(err) - } - apps := []string{"argo", "argocd", "vault"} - cb := make(map[string]string) - cb["argo"] = fmt.Sprintf("https://argo.%s/oauth2/callback", domain) - cb["argocd"] = fmt.Sprintf("https://argocd.%s/auth/callback", domain) - cb["vault"] = fmt.Sprintf("https://vault.%s:8250/oidc/callback http://localhost:8250/oidc/callback https://vault.%s/ui/vault/auth/oidc/oidc/callback http://localhost:8200/ui/vault/auth/oidc/oidc/callback", domain, domain) - for _, app := range apps { - log.Println("checking to see if", app, "oidc application needs to be created in gitlab") - appId := viper.GetString(fmt.Sprintf("gitlab.oidc.%s.applicationid", app)) - if appId == "" { - // Create an application - opts := &gitlab.CreateApplicationOptions{ - Name: gitlab.String(app), - RedirectURI: gitlab.String(cb[app]), - Scopes: gitlab.String("read_user openid email"), - } - createdApp, _, err := git.Applications.CreateApplication(opts) - if err != nil { - log.Fatal(err) - } - // List all applications - existingApps, _, err := git.Applications.ListApplications(&gitlab.ListApplicationsOptions{}) - if err != nil { - log.Fatal(err) - } - created := false - for _, existingApp := range existingApps { - if existingApp.ApplicationName == app { - created = true - } - } - if created { - log.Println("created gitlab oidc application with applicationid", createdApp.ApplicationID) - viper.Set(fmt.Sprintf("gitlab.oidc.%s.applicationid", app), createdApp.ApplicationID) - viper.Set(fmt.Sprintf("gitlab.oidc.%s.secret", app), createdApp.Secret) - - secretData := map[string]interface{}{ - "data": map[string]interface{}{ - "application_id": createdApp.ApplicationID, - "secret": createdApp.Secret, - }, - } - secretPath := fmt.Sprintf("secret/data/oidc/%s", app) - addVaultSecret(secretPath, secretData) - viper.WriteConfig() - } else { - log.Panic("could not create gitlab iodc application", app) - } - } - } -} - -func addVaultSecret(secretPath string, secretData map[string]interface{}) { - log.Println("vault called") - - config := vault.DefaultConfig() - - config.Address = fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname")) - - client, err := vault.NewClient(config) - if err != nil { - log.Println("unable to initialize Vault client: ", err) - } - - client.SetToken(viper.GetString("vault.token")) - - // Writing a secret - _, err = client.Logical().Write(secretPath, secretData) - if err != nil { - log.Println("unable to write secret: ", err) - } else { - log.Println("secret written successfully.") - } -} - -func configureVault() { - if !viper.GetBool("create.terraformapplied.vault") { - if dryrunMode { - log.Printf("[#99] Dry-run mode, configureVault skipped.") - return - } - // ``` - // NOTE: the terraform here produces unnecessary $var.varname vars in the atlantis secret for nonsensitive values - // the following atlantis secrets shouldn't have vars in the gitops source code for the atlantis secret, they - // should look like us-east-1, in flat string code as non-sensitive vals - refactor soon. - // "TF_VAR_aws_region": "us-east-1", - // "TF_VAR_aws_account_id": "${var.aws_account_id}", - // "TF_VAR_email_address": "${var.email_address}", - // "TF_VAR_hosted_zone_id": "${var.hosted_zone_id}", - // "TF_VAR_hosted_zone_name": "${var.hosted_zone_name}", - // "TF_VAR_vault_addr": "${var.vault_addr}", - // ``` - // ... obviously keep the sensitive values bound to vars - - //TODO replace this command: - var outb, errb bytes.Buffer - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "get", "secret", "vault-unseal-keys", "-o", "jsonpath='{.data.cluster-keys\\.json}'") - k.Stdout = &outb - k.Stderr = &errb - err := k.Run() - if err != nil { - log.Println("failed to call k.Run() to get gitlab pod: ", err) - } - vaultKeysEncoded := outb.String() - vaultKeysEncoded = strings.Replace(vaultKeysEncoded, "'", "", -1) - log.Println("vault keys", vaultKeysEncoded) - - vaultKeysBytes, err := base64.StdEncoding.DecodeString(vaultKeysEncoded) - log.Println(vaultKeysBytes) - if err != nil { - panic(err) - } - vaultKeys := string(vaultKeysBytes) - log.Println(vaultKeys) - - var dat map[string]interface{} - if err := json.Unmarshal([]byte(vaultKeys), &dat); err != nil { - panic(err) - } - vaultToken := dat["root_token"].(string) - log.Println(vaultToken) - viper.Set("vault.token", vaultToken) - viper.WriteConfig() - - // Prepare for terraform vault execution - os.Setenv("VAULT_ADDR", fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname"))) - os.Setenv("VAULT_TOKEN", vaultToken) - - os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname"))) - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_email_address", viper.GetString("adminemail")) - os.Setenv("TF_VAR_gitlab_runner_token", viper.GetString("gitlab.runnertoken")) - os.Setenv("TF_VAR_gitlab_token", viper.GetString("gitlab.token")) - os.Setenv("TF_VAR_hosted_zone_id", viper.GetString("aws.domainid")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) - os.Setenv("TF_VAR_vault_token", viper.GetString("aws.domainname")) - os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") - - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", home) - err = os.Chdir(directory) - if err != nil { - log.Println("error changing dir") - } - - tfInitCmd := exec.Command(terraformPath, "init") - tfInitCmd.Stdout = os.Stdout - tfInitCmd.Stderr = os.Stderr - err = tfInitCmd.Run() - if err != nil { - log.Println("failed to call vault terraform init: ", err) - } - - tfApplyCmd := exec.Command(terraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") - tfApplyCmd.Stdout = os.Stdout - tfApplyCmd.Stderr = os.Stderr - err = tfApplyCmd.Run() - if err != nil { - log.Println("failed to call vault terraform apply: ", err) - } - - viper.Set("create.terraformapplied.vault", true) - viper.WriteConfig() - } else { - log.Println("Skipping: configureVault") - } -} - -func awaitGitlab() { - if dryrunMode { - log.Printf("[#99] Dry-run mode, awaitGitlab skipped.") - return - } - log.Println("awaitGitlab called") - max := 200 - for i := 0; i < max; i++ { - - // todo should this be aws.hostedzonedname since we're sticking to an - // todo aws: and gcp: figure their nomenclature is more familar - hostedZoneName := viper.GetString("aws.domainname") - - resp, _ := http.Get(fmt.Sprintf("https://gitlab.%s", hostedZoneName)) - if resp != nil && resp.StatusCode == 200 { - log.Println("gitlab host resolved, 30 second grace period required...") - time.Sleep(time.Second * 30) - i = max - } else { - log.Println("gitlab host not resolved, sleeping 10s") - time.Sleep(time.Second * 10) - } - } -} func init() { rootCmd.AddCommand(createCmd) diff --git a/cmd/files.go b/cmd/files.go new file mode 100644 index 000000000..81ce2a4b0 --- /dev/null +++ b/cmd/files.go @@ -0,0 +1,250 @@ +package cmd + +import ( + "io" + "fmt" + "log" + "os" + "archive/tar" + "archive/zip" + "compress/gzip" + "net/http" + "path/filepath" + "strings" +) + +func download() { + toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) + + err := os.Mkdir(toolsDir, 0777) + if err != nil { + log.Println("error creating directory %s", toolsDir, err) + } + + kubectlVersion := "v1.20.0" + kubectlDownloadUrl := fmt.Sprintf("https://dl.k8s.io/release/%s/bin/%s/%s/kubectl", kubectlVersion, localOs, localArchitecture) + downloadFile(kubectlClientPath, kubectlDownloadUrl) + os.Chmod(kubectlClientPath, 0755) + + // todo this kubeconfig is not available to us until we have run the terraform in base/ + os.Setenv("KUBECONFIG", kubeconfigPath) + log.Println("going to print the kubeconfig env in runtime", os.Getenv("KUBECONFIG")) + + kubectlStdOut, kubectlStdErr,errKubectl := execShellReturnStrings(kubectlClientPath, "version", "--client", "--short") + log.Printf("-> kubectl version:\n\t%s\n\t%s\n",kubectlStdOut,kubectlStdErr) + if errKubectl != nil { + log.Println("failed to call kubectlVersionCmd.Run(): %v", err) + } + + Trackers[trackerStage5].Tracker.Increment(int64(1)) + // argocdVersion := "v2.3.4" + // argocdDownloadUrl := fmt.Sprintf("https://github.com/argoproj/argo-cd/releases/download/%s/argocd-%s-%s", argocdVersion, localOs, localArchitecture) + // argocdClientPath := fmt.Sprintf("%s/.kubefirst/tools/argocd", home) + // downloadFile(argocdClientPath, argocdDownloadUrl) + // os.Chmod(argocdClientPath, 755) + + // argocdVersionCmd := exec.Command(argocdClientPath, "version", "--client", "--short") + // argocdVersionCmd.Stdout = os.Stdout + // argocdVersionCmd.Stderr = os.Stderr + // err = argocdVersionCmd.Run() + // if err != nil { + // fmt.Println("failed to call argocdVersionCmd.Run(): %v", err) + // } + + // todo adopt latest helmVersion := "v3.9.0" + terraformVersion := "1.0.11" + // terraformClientPath := fmt.Sprintf("./%s-%s/terraform", localOs, localArchitecture) + terraformDownloadUrl := fmt.Sprintf("https://releases.hashicorp.com/terraform/%s/terraform_%s_%s_%s.zip", terraformVersion, terraformVersion, localOs, localArchitecture) + terraformDownloadZipPath := fmt.Sprintf("%s/.kubefirst/tools/terraform.zip", home) + downloadFile(terraformDownloadZipPath, terraformDownloadUrl) + // terraformZipDownload, err := os.Open(terraformDownloadZipPath) + if err != nil { + log.Println("error reading terraform file") + } + unzipDirectory := fmt.Sprintf("%s/.kubefirst/tools", home) + unzip(terraformDownloadZipPath, unzipDirectory) + + os.Chmod(unzipDirectory, 0777) + os.Chmod(fmt.Sprintf("%s/terraform", unzipDirectory), 0755) + Trackers[trackerStage5].Tracker.Increment(int64(1)) + + // todo adopt latest helmVersion := "v3.9.0" + helmVersion := "v3.2.1" + helmDownloadUrl := fmt.Sprintf("https://get.helm.sh/helm-%s-%s-%s.tar.gz", helmVersion, localOs, localArchitecture) + helmDownloadTarGzPath := fmt.Sprintf("%s/.kubefirst/tools/helm.tar.gz", home) + downloadFile(helmDownloadTarGzPath, helmDownloadUrl) + helmTarDownload, err := os.Open(helmDownloadTarGzPath) + if err != nil { + log.Println("error reading helm file") + } + extractFileFromTarGz(helmTarDownload, fmt.Sprintf("%s-%s/helm", localOs, localArchitecture), helmClientPath) + os.Chmod(helmClientPath, 0755) + helmStdOut, helmStdErr,errHelm := execShellReturnStrings(helmClientPath, "version", "--client", "--short") + log.Printf("-> kubectl version:\n\t%s\n\t%s\n",helmStdOut,helmStdErr) + // currently argocd init values is generated by flare nebulous ssh + // todo helm install argocd --create-namespace --wait --values ~/.kubefirst/argocd-init-values.yaml argo/argo-cd + if errHelm != nil { + log.Println("failed to call helmVersionCmd.Run(): %v", err) + } + Trackers[trackerStage5].Tracker.Increment(int64(1)) + +} + +func downloadFile(filepath string, url string) (err error) { + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Get the data + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Check server response + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("bad status: %s", resp.Status) + } + + // Writer the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + return nil +} +func extractFileFromTarGz(gzipStream io.Reader, tarAddress string, targetFilePath string) { + uncompressedStream, err := gzip.NewReader(gzipStream) + if err != nil { + log.Fatal("extractTarGz: NewReader failed") + } + + tarReader := tar.NewReader(uncompressedStream) + + for { + header, err := tarReader.Next() + if err == io.EOF { + break + } + if err != nil { + log.Println("extractTarGz: Next() failed: %s", err.Error()) + } + log.Println(header.Name) + if header.Name == tarAddress { + switch header.Typeflag { + case tar.TypeReg: + outFile, err := os.Create(targetFilePath) + if err != nil { + log.Println("extractTarGz: Create() failed: %s", err.Error()) + } + if _, err := io.Copy(outFile, tarReader); err != nil { + log.Println("extractTarGz: Copy() failed: %s", err.Error()) + } + outFile.Close() + + default: + log.Println( + "extractTarGz: uknown type: %s in %s", + header.Typeflag, + header.Name) + } + + } + } +} + +func extractTarGz(gzipStream io.Reader) { + uncompressedStream, err := gzip.NewReader(gzipStream) + if err != nil { + log.Fatal("extractTarGz: NewReader failed") + } + + tarReader := tar.NewReader(uncompressedStream) + + for { + header, err := tarReader.Next() + + if err == io.EOF { + break + } + if err != nil { + log.Println("extractTarGz: Next() failed: %s", err.Error()) + } + p, _ := filepath.Abs(header.Name) + if !strings.Contains(p, "..") { + + switch header.Typeflag { + case tar.TypeDir: + if err := os.Mkdir(header.Name, 0755); err != nil { + log.Println("extractTarGz: Mkdir() failed: %s", err.Error()) + } + case tar.TypeReg: + outFile, err := os.Create(header.Name) + if err != nil { + log.Println("extractTarGz: Create() failed: %s", err.Error()) + } + if _, err := io.Copy(outFile, tarReader); err != nil { + log.Println("extractTarGz: Copy() failed: %s", err.Error()) + } + outFile.Close() + + default: + log.Println( + "extractTarGz: uknown type: %s in %s", + header.Typeflag, + header.Name) + } + } + + } +} + +func unzip(zipFilepath string, unzipDirectory string) { + dst := unzipDirectory + archive, err := zip.OpenReader(zipFilepath) + if err != nil { + panic(err) + } + defer archive.Close() + + for _, f := range archive.File { + filePath := filepath.Join(dst, f.Name) + log.Println("unzipping file ", filePath) + + if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) { + log.Println("invalid file path") + return + } + if f.FileInfo().IsDir() { + log.Println("creating directory...") + os.MkdirAll(filePath, os.ModePerm) + continue + } + + if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { + panic(err) + } + + dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + panic(err) + } + + fileInArchive, err := f.Open() + if err != nil { + panic(err) + } + + if _, err := io.Copy(dstFile, fileInArchive); err != nil { + panic(err) + } + + dstFile.Close() + fileInArchive.Close() + } +} \ No newline at end of file diff --git a/cmd/init.go b/cmd/init.go index 194644e2e..91777954e 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -5,41 +5,16 @@ Copyright © 2022 NAME HERE package cmd import ( - "archive/tar" - "archive/zip" - "compress/gzip" - "context" "fmt" - "io" "io/ioutil" "log" - "net" - "net/http" "os" - "os/exec" - "path/filepath" - "strconv" "strings" "time" - - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/route53" - "github.com/aws/aws-sdk-go-v2/service/route53/types" - "github.com/aws/aws-sdk-go-v2/service/sts" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/cip8/autoname" - "github.com/go-git/go-git/v5" - gitConfig "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing/object" - gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/kubefirst/nebulous/pkg/flare" - gitlabSsh "github.com/kubefirst/nebulous/pkg/ssh" - ssh2 "golang.org/x/crypto/ssh" + gitlabSsh "github.com/kubefirst/nebulous/pkg/ssh" ) var Trackers map[string]*flare.ActionTracker @@ -205,799 +180,22 @@ func init() { } -func bucketRand() { - sess, err := session.NewSession(&aws.Config{ - Region: aws.String(viper.GetString("aws.region"))}, - ) - if err != nil { - log.Println("failed to attempt bucket creation ", err.Error()) - os.Exit(1) - } - - s3Client := s3.New(sess) - - randomName := strings.ReplaceAll(autoname.Generate(), "_", "-") - viper.Set("bucket.rand", randomName) - - buckets := strings.Fields("state-store argo-artifacts gitlab-backup chartmuseum") - for _, bucket := range buckets { - bucketExists := viper.GetBool(fmt.Sprintf("bucket.%s.created", bucket)) - if !bucketExists { - bucketName := fmt.Sprintf("k1-%s-%s", bucket, randomName) - - log.Println("creating", bucket, "bucket", bucketName) - - regionName := viper.GetString("aws.region") - log.Println("region is ", regionName) - if !dryrunMode { - if regionName == "us-east-1" { - _, err = s3Client.CreateBucket(&s3.CreateBucketInput{ - Bucket: &bucketName, - }) - } else { - _, err = s3Client.CreateBucket(&s3.CreateBucketInput{ - Bucket: &bucketName, - CreateBucketConfiguration: &s3.CreateBucketConfiguration{ - LocationConstraint: aws.String(regionName), - }, - }) - } - if err != nil { - log.Println("failed to create bucket "+bucketName, err.Error()) - os.Exit(1) - } - } else { - log.Printf("[#99] Dry-run mode, bucket creation skipped: %s", bucketName) - } - viper.Set(fmt.Sprintf("bucket.%s.created", bucket), true) - viper.Set(fmt.Sprintf("bucket.%s.name", bucket), bucketName) - viper.WriteConfig() - } - log.Printf("bucket %s exists", viper.GetString(fmt.Sprintf("bucket.%s.name", bucket))) - Trackers[trackerStage7].Tracker.Increment(int64(1)) - } -} - -func getAccountInfo() { - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - log.Println("failed to load configuration, error:", err) - } - // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration - stsClient := sts.NewFromConfig(cfg) - iamCaller, err := stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{}) - if err != nil { - log.Println("oh no error on call", err) - } - - viper.Set("aws.accountid", *iamCaller.Account) - viper.Set("aws.userarn", *iamCaller.Arn) - viper.WriteConfig() -} - -func testHostedZoneLiveness(hostedZoneName, hostedZoneId string) { - //tracker := progress.Tracker{Message: "testing hosted zone", Total: 25} - - // todo need to create single client and pass it - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - log.Println("failed to load configuration, error:", err) - } - // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration - route53Client := route53.NewFromConfig(cfg) - - // todo when checking to see if hosted zone exists print ns records for user to verity in dns registrar - route53RecordName := fmt.Sprintf("kubefirst-liveness.%s", hostedZoneName) - route53RecordValue := "domain record propagated" - - log.Println("checking to see if record", route53RecordName, "exists") - log.Println("hostedZoneId", hostedZoneId) - log.Println("route53RecordName", route53RecordName) - - recordList, err := route53Client.ListResourceRecordSets(context.TODO(), &route53.ListResourceRecordSetsInput{ - HostedZoneId: aws.String(hostedZoneId), - StartRecordName: aws.String(route53RecordName), - StartRecordType: "TXT", - }) - if err != nil { - log.Println("failed read route53 ", err.Error()) - os.Exit(1) - } - - if len(recordList.ResourceRecordSets) == 0 { - if !dryrunMode { - record, err := route53Client.ChangeResourceRecordSets(context.TODO(), &route53.ChangeResourceRecordSetsInput{ - ChangeBatch: &types.ChangeBatch{ - Changes: []types.Change{ - { - Action: "CREATE", - ResourceRecordSet: &types.ResourceRecordSet{ - Name: aws.String(route53RecordName), - Type: "TXT", - ResourceRecords: []types.ResourceRecord{ - { - Value: aws.String(strconv.Quote(route53RecordValue)), - }, - }, - TTL: aws.Int64(10), - Weight: aws.Int64(100), - SetIdentifier: aws.String("CREATE sanity check for kubefirst installation"), - }, - }, - }, - Comment: aws.String("CREATE sanity check dns record."), - }, - HostedZoneId: aws.String(hostedZoneId), - }) - if err != nil { - log.Println(err) - } - log.Println("record creation status is ", record.ChangeInfo.Status) - } else { - log.Printf("[#99] Dry-run mode, route53 creation/update skipped: %s", route53RecordName) - } - } - count := 0 - // todo need to exit after n number of minutes and tell them to check ns records - // todo this logic sucks - for count <= 25 { - count++ - //tracker.Increment(1) - //log.Println(text.Faint.Sprintf("[INFO] dns test %d of 25", count)) - - log.Println(route53RecordName) - ips, err := net.LookupTXT(route53RecordName) - - log.Println(ips) - - if err != nil { - // tracker.Message = fmt.Sprintln("dns test", count, "of", 25) - fmt.Fprintf(os.Stderr, "Could not get record name %s - waiting 10 seconds and trying again: %v\n", route53RecordName, err) - time.Sleep(10 * time.Second) - } else { - for _, ip := range ips { - // todo check ip against route53RecordValue in some capacity so we can pivot the value for testing - log.Printf("%s. in TXT record value: %s\n", route53RecordName, ip) - //tracker.MarkAsDone() - count = 26 - } - } - if count == 25 { - log.Println("unable to resolve hosted zone dns record. please check your domain registrar") - //tracker.MarkAsErrored() - //pw.Stop() - os.Exit(1) - } - } - // todo delete route53 record - - // recordDelete, err := route53Client.ChangeResourceRecordSets(context.TODO(), &route53.ChangeResourceRecordSetsInput{ - // ChangeBatch: &types.ChangeBatch{ - // Changes: []types.Change{ - // { - // Action: "DELETE", - // ResourceRecordSet: &types.ResourceRecordSet{ - // Name: aws.String(route53RecordName), - // Type: "A", - // ResourceRecords: []types.ResourceRecord{ - // { - // Value: aws.String(route53RecordValue), - // }, - // }, - // TTL: aws.Int64(10), - // Weight: aws.Int64(100), - // SetIdentifier: aws.String("CREATE sanity check for kubefirst installation"), - // }, - // }, - // }, - // Comment: aws.String("CREATE sanity check dns record."), - // }, - // HostedZoneId: aws.String(hostedZoneId), - // }) - // if err != nil { - // fmt.Println("error deleting route 53 record after liveness test") - // } - // fmt.Println("record deletion status is ", *&recordDelete.ChangeInfo.Status) - -} - -func modConfigYaml() { - - file, err := ioutil.ReadFile("./config.yaml") - if err != nil { - log.Println("error reading file", err) - } - - newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) - - err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) - if err != nil { - panic(err) - } -} - -func getDNSInfo(hostedZoneName string) string { - - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - log.Println("failed to load configuration, error:", err) - } - // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration - route53Client := route53.NewFromConfig(cfg) - hostedZones, err := route53Client.ListHostedZonesByName(context.TODO(), &route53.ListHostedZonesByNameInput{ - DNSName: &hostedZoneName, - }) - if err != nil { - log.Println("oh no error on call", err) - } - - var zoneId string - - for _, zone := range hostedZones.HostedZones { - if *zone.Name == fmt.Sprintf(`%s%s`, hostedZoneName, ".") { - zoneId = returnHostedZoneId(*zone.Id) - log.Printf(`found entry for user submitted domain %s, using hosted zone id %s`, hostedZoneName, zoneId) - viper.Set("aws.domainname", hostedZoneName) - viper.Set("aws.domainid", zoneId) - viper.WriteConfig() - } - } - return zoneId - -} - -func returnHostedZoneId(rawZoneId string) string { - return strings.Split(rawZoneId, "/")[2] -} - -func publicKey() (*ssh.PublicKeys, error) { - var publicKey *ssh.PublicKeys - publicKey, err := ssh.NewPublicKeys("git", []byte(viper.GetString("botprivatekey")), "") - if err != nil { - return nil, err - } - return publicKey, err -} - -func cloneGitOpsRepo() { - - url := "https://github.com/kubefirst/gitops-template" - directory := fmt.Sprintf("%s/.kubefirst/gitops", home) - - // Clone the given repository to the given directory - log.Println("git clone", url, directory) - - _, err := git.PlainClone(directory, false, &git.CloneOptions{ - URL: url, - }) - if err != nil { - log.Println(err) - } - - println("downloaded gitops repo from template to directory", home, "/.kubefirst/gitops") -} - -func configureSoftServe() { - // todo clone repo - // todo manipulate config.yaml - // todo git add / commit / push - url := "ssh://127.0.0.1:8022/config" - directory := fmt.Sprintf("%s/.kubefirst/config", home) - - // Clone the given repository to the given directory - log.Println("git clone", url, directory) - - auth, _ := publicKey() - - auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() - - repo, err := git.PlainClone(directory, false, &git.CloneOptions{ - URL: url, - Auth: auth, - }) - if err != nil { - log.Println("error!, ", err) - } - - file, err := ioutil.ReadFile(fmt.Sprintf("%s/config.yaml", directory)) - if err != nil { - log.Println("error reading file", err) - } - - newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) - - err = ioutil.WriteFile(fmt.Sprintf("%s/config.yaml", directory), []byte(newFile), 0) - if err != nil { - panic(err) - } - - println("re-wrote config.yaml", home, "/.kubefirst/config") - - w, _ := repo.Worktree() - - log.Println("Committing new changes...") - w.Add(".") - w.Commit("updating soft-serve server config", &git.CommitOptions{ - Author: &object.Signature{ - Name: "kubefirst-bot", - Email: installerEmail, - When: time.Now(), - }, - }) - - err = repo.Push(&git.PushOptions{ - RemoteName: "origin", - Auth: auth, - }) - if err != nil { - log.Println("error pushing to remote", err) - } -} -func pushGitopsToSoftServe() { - directory := fmt.Sprintf("%s/.kubefirst/gitops", home) - // // Clone the given repository to the given directory - log.Println("open %s git repo", directory) - repo, err := git.PlainOpen(directory) - if err != nil { - log.Println("error opening the directory ", directory, err) - } - log.Println("git remote add origin ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops") - _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ - Name: "soft", - URLs: []string{"ssh://127.0.0.1:8022/gitops"}, - }) - if err != nil { - log.Println("Error creating remote repo:", err) - os.Exit(1) - } - w, _ := repo.Worktree() - log.Println("Committing new changes...") - w.Add(".") - w.Commit("setting new remote upstream to soft-serve", &git.CommitOptions{ - Author: &object.Signature{ - Name: "kubefirst-bot", - Email: installerEmail, - When: time.Now(), - }, - }) - auth, _ := publicKey() - auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() - err = repo.Push(&git.PushOptions{ - RemoteName: "soft", - Auth: auth, - }) - if err != nil { - log.Println("error pushing to remote", err) - } -} -func pushGitopsToGitLab() { - if dryrunMode { - log.Printf("[#99] Dry-run mode, pushGitopsToGitLab skipped.") - return - } - - //TODO: should this step to be skipped if already executed? - domain := viper.GetString("aws.domainname") - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) - directory := fmt.Sprintf("%s/.kubefirst/gitops", home) - repo, err := git.PlainOpen(directory) - if err != nil { - log.Println("error opening the directory ", directory, err) - } - - //upstream := fmt.Sprintf("ssh://gitlab.%s:22:kubefirst/gitops", viper.GetString("aws.domainname")) - // upstream := "git@gitlab.kube1st.com:kubefirst/gitops.git" - upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) - log.Println("git remote add gitlab at url", upstream) - - _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ - Name: "gitlab", - URLs: []string{upstream}, - }) - if err != nil { - log.Println("Error creating remote repo:", err) - } - w, _ := repo.Worktree() - os.RemoveAll(directory + "/terraform/base/.terraform") - os.RemoveAll(directory + "/terraform/gitlab/.terraform") - os.RemoveAll(directory + "/terraform/vault/.terraform") - log.Println("Committing new changes...") - w.Add(".") - _, err = w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ - Author: &object.Signature{ - Name: "kubefirst-bot", - Email: installerEmail, - When: time.Now(), - }, - }) - if err != nil { - log.Println("error committing changes", err) - } - - log.Println("setting auth...") - // auth, _ := publicKey() - // auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() - - auth := &gitHttp.BasicAuth{ - Username: "root", - Password: viper.GetString("gitlab.token"), - } - - err = repo.Push(&git.PushOptions{ - RemoteName: "gitlab", - Auth: auth, - }) - if err != nil { - log.Println("error pushing to remote", err) - } - -} - -func detokenize(path string) { - - err := filepath.Walk(path, detokenizeDirectory) - if err != nil { - panic(err) - } -} - -func detokenizeDirectory(path string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - - if fi.IsDir() { - return nil // - } - - if strings.Contains(path, ".git") || strings.Contains(path, ".terraform") { - return nil - } - - matched, err := filepath.Match("*", fi.Name()) - - if err != nil { - panic(err) - } - - if matched { - read, err := ioutil.ReadFile(path) - if err != nil { - panic(err) - } - // todo should detokenize be a switch statement based on a value found in viper? - gitlabConfigured := viper.GetBool("gitlab.keyuploaded") - - newContents := "" - - if gitlabConfigured { - newContents = strings.Replace(string(read), "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.domainname")), -1) - } else { - newContents = strings.Replace(string(read), "https://gitlab./kubefirst/gitops.git", "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", -1) - } - - botPublicKey := viper.GetString("botpublickey") - domainId := viper.GetString("aws.domainid") - domainName := viper.GetString("aws.domainname") - bucketStateStore := viper.GetString("bucket.state-store.name") - bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") - bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") - bucketChartmuseum := viper.GetString("bucket.chartmuseum.name") - region := viper.GetString("aws.region") - adminEmail := viper.GetString("adminemail") - awsAccountId := viper.GetString("aws.accountid") - kmsKeyId := viper.GetString("vault.kmskeyid") - - newContents = strings.Replace(newContents, "", strings.TrimSpace(botPublicKey), -1) - newContents = strings.Replace(newContents, "", bucketStateStore, -1) - newContents = strings.Replace(newContents, "", bucketArgoArtifacts, -1) - newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) - newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) - newContents = strings.Replace(newContents, "", domainId, -1) - newContents = strings.Replace(newContents, "", domainName, -1) - newContents = strings.Replace(newContents, "", region, -1) - newContents = strings.Replace(newContents, "", adminEmail, -1) - newContents = strings.Replace(newContents, "", awsAccountId, -1) - if kmsKeyId != "" { - newContents = strings.Replace(newContents, "", kmsKeyId, -1) - } - - if viper.GetBool("create.terraformapplied.gitlab") { - newContents = strings.Replace(newContents, "", domainName, -1) - newContents = strings.Replace(newContents, "", region, -1) - newContents = strings.Replace(newContents, "", awsAccountId, -1) - } - - err = ioutil.WriteFile(path, []byte(newContents), 0) - if err != nil { - panic(err) - } - - } - - return nil -} - -func download() { - toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) - - err := os.Mkdir(toolsDir, 0777) - if err != nil { - log.Println("error creating directory %s", toolsDir, err) - } - - kubectlVersion := "v1.20.0" - kubectlDownloadUrl := fmt.Sprintf("https://dl.k8s.io/release/%s/bin/%s/%s/kubectl", kubectlVersion, localOs, localArchitecture) - downloadFile(kubectlClientPath, kubectlDownloadUrl) - os.Chmod(kubectlClientPath, 0755) - - // todo this kubeconfig is not available to us until we have run the terraform in base/ - os.Setenv("KUBECONFIG", kubeconfigPath) - log.Println("going to print the kubeconfig env in runtime", os.Getenv("KUBECONFIG")) - - kubectlStdOut, kubectlStdErr,errKubectl := execShellReturnStrings(kubectlClientPath, "version", "--client", "--short") - log.Printf("-> kubectl version:\n\t%s\n\t%s\n",kubectlStdOut,kubectlStdErr) - if errKubectl != nil { - log.Println("failed to call kubectlVersionCmd.Run(): %v", err) - } - - Trackers[trackerStage5].Tracker.Increment(int64(1)) - // argocdVersion := "v2.3.4" - // argocdDownloadUrl := fmt.Sprintf("https://github.com/argoproj/argo-cd/releases/download/%s/argocd-%s-%s", argocdVersion, localOs, localArchitecture) - // argocdClientPath := fmt.Sprintf("%s/.kubefirst/tools/argocd", home) - // downloadFile(argocdClientPath, argocdDownloadUrl) - // os.Chmod(argocdClientPath, 755) - - // argocdVersionCmd := exec.Command(argocdClientPath, "version", "--client", "--short") - // argocdVersionCmd.Stdout = os.Stdout - // argocdVersionCmd.Stderr = os.Stderr - // err = argocdVersionCmd.Run() - // if err != nil { - // fmt.Println("failed to call argocdVersionCmd.Run(): %v", err) - // } - - // todo adopt latest helmVersion := "v3.9.0" - terraformVersion := "1.0.11" - // terraformClientPath := fmt.Sprintf("./%s-%s/terraform", localOs, localArchitecture) - terraformDownloadUrl := fmt.Sprintf("https://releases.hashicorp.com/terraform/%s/terraform_%s_%s_%s.zip", terraformVersion, terraformVersion, localOs, localArchitecture) - terraformDownloadZipPath := fmt.Sprintf("%s/.kubefirst/tools/terraform.zip", home) - downloadFile(terraformDownloadZipPath, terraformDownloadUrl) - // terraformZipDownload, err := os.Open(terraformDownloadZipPath) - if err != nil { - log.Println("error reading terraform file") - } - unzipDirectory := fmt.Sprintf("%s/.kubefirst/tools", home) - unzip(terraformDownloadZipPath, unzipDirectory) - - os.Chmod(unzipDirectory, 0777) - os.Chmod(fmt.Sprintf("%s/terraform", unzipDirectory), 0755) - Trackers[trackerStage5].Tracker.Increment(int64(1)) - - // todo adopt latest helmVersion := "v3.9.0" - helmVersion := "v3.2.1" - helmDownloadUrl := fmt.Sprintf("https://get.helm.sh/helm-%s-%s-%s.tar.gz", helmVersion, localOs, localArchitecture) - helmDownloadTarGzPath := fmt.Sprintf("%s/.kubefirst/tools/helm.tar.gz", home) - downloadFile(helmDownloadTarGzPath, helmDownloadUrl) - helmTarDownload, err := os.Open(helmDownloadTarGzPath) - if err != nil { - log.Println("error reading helm file") - } - extractFileFromTarGz(helmTarDownload, fmt.Sprintf("%s-%s/helm", localOs, localArchitecture), helmClientPath) - os.Chmod(helmClientPath, 0755) - helmStdOut, helmStdErr,errHelm := execShellReturnStrings(helmClientPath, "version", "--client", "--short") - log.Printf("-> kubectl version:\n\t%s\n\t%s\n",helmStdOut,helmStdErr) - // currently argocd init values is generated by flare nebulous ssh - // todo helm install argocd --create-namespace --wait --values ~/.kubefirst/argocd-init-values.yaml argo/argo-cd - if errHelm != nil { - log.Println("failed to call helmVersionCmd.Run(): %v", err) - } - Trackers[trackerStage5].Tracker.Increment(int64(1)) - -} - -func downloadFile(filepath string, url string) (err error) { - // Create the file - out, err := os.Create(filepath) - if err != nil { - return err - } - defer out.Close() - - // Get the data - resp, err := http.Get(url) - if err != nil { - return err - } - defer resp.Body.Close() - - // Check server response - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("bad status: %s", resp.Status) - } - - // Writer the body to file - _, err = io.Copy(out, resp.Body) - if err != nil { - return err - } - - return nil -} -func extractFileFromTarGz(gzipStream io.Reader, tarAddress string, targetFilePath string) { - uncompressedStream, err := gzip.NewReader(gzipStream) - if err != nil { - log.Fatal("extractTarGz: NewReader failed") - } - - tarReader := tar.NewReader(uncompressedStream) - - for { - header, err := tarReader.Next() - if err == io.EOF { - break - } - if err != nil { - log.Println("extractTarGz: Next() failed: %s", err.Error()) - } - log.Println(header.Name) - if header.Name == tarAddress { - switch header.Typeflag { - case tar.TypeReg: - outFile, err := os.Create(targetFilePath) - if err != nil { - log.Println("extractTarGz: Create() failed: %s", err.Error()) - } - if _, err := io.Copy(outFile, tarReader); err != nil { - log.Println("extractTarGz: Copy() failed: %s", err.Error()) - } - outFile.Close() - - default: - log.Println( - "extractTarGz: uknown type: %s in %s", - header.Typeflag, - header.Name) - } - - } - } -} - -func extractTarGz(gzipStream io.Reader) { - uncompressedStream, err := gzip.NewReader(gzipStream) - if err != nil { - log.Fatal("extractTarGz: NewReader failed") - } - - tarReader := tar.NewReader(uncompressedStream) - - for { - header, err := tarReader.Next() - - if err == io.EOF { - break - } - if err != nil { - log.Println("extractTarGz: Next() failed: %s", err.Error()) - } - p, _ := filepath.Abs(header.Name) - if !strings.Contains(p, "..") { - - switch header.Typeflag { - case tar.TypeDir: - if err := os.Mkdir(header.Name, 0755); err != nil { - log.Println("extractTarGz: Mkdir() failed: %s", err.Error()) - } - case tar.TypeReg: - outFile, err := os.Create(header.Name) - if err != nil { - log.Println("extractTarGz: Create() failed: %s", err.Error()) - } - if _, err := io.Copy(outFile, tarReader); err != nil { - log.Println("extractTarGz: Copy() failed: %s", err.Error()) - } - outFile.Close() - - default: - log.Println( - "extractTarGz: uknown type: %s in %s", - header.Typeflag, - header.Name) - } - } - - } -} - -func createSoftServe(kubeconfigPath string) { - createSoftServeFlag := viper.GetBool("create.softserve.create") - if createSoftServeFlag != true { - log.Println("Executing createSoftServe") - if dryrunMode { - log.Printf("[#99] Dry-run mode, createSoftServe skipped.") - return - } - toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) - - err := os.Mkdir(toolsDir, 0777) - if err != nil { - log.Println("error creating directory %s", toolsDir, err) - } - - // create soft-serve stateful set - softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", home) - softServeApplyOut, softServeApplyErr,errSoftServeApply := execShellReturnStrings(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") - log.Printf("Result:\n\t%s\n\t%s\n",softServeApplyOut,softServeApplyErr) - if errSoftServeApply != nil { - log.Println("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) - } - - viper.Set("create.softserve.create", true) - viper.WriteConfig() - log.Println("waiting for soft-serve installation to complete...") - time.Sleep(60 * time.Second) - //TODO: Update mechanism of waiting - } else { - log.Println("Skipping: createSoftServe") - } - -} - -func helmInstallArgocd(home string, kubeconfigPath string) { - argocdCreated := viper.GetBool("create.argocd.helm") - if !argocdCreated { - if dryrunMode { - log.Printf("[#99] Dry-run mode, helmInstallArgocd skipped.") - return - } - helmClientPath := fmt.Sprintf("%s/.kubefirst/tools/helm", home) - - // ! commenting out until a clean execution is necessary // create namespace - helmRepoAddArgocd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") - helmRepoAddArgocd.Stdout = os.Stdout - helmRepoAddArgocd.Stderr = os.Stderr - err := helmRepoAddArgocd.Run() - if err != nil { - log.Println("failed to call helmRepoAddArgocd.Run(): %v", err) - } - - helmRepoUpdate := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "update") - helmRepoUpdate.Stdout = os.Stdout - helmRepoUpdate.Stderr = os.Stderr - err = helmRepoUpdate.Run() - if err != nil { - log.Println("failed to call helmRepoUpdate.Run(): %v", err) - } - - helmInstallArgocdOut, helmInstallArgocdErr,errHelmInstallArgocd := execShellReturnStrings(helmClientPath, "--kubeconfig", kubeconfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") - log.Printf("Result:\n\t%s\n\t%s\n",helmInstallArgocdOut,helmInstallArgocdErr) - if errHelmInstallArgocd != nil { - log.Println("failed to call helmInstallArgocdCmd.Run(): %v", err) - } - - viper.Set("create.argocd.helm", true) - err = viper.WriteConfig() - if err != nil { - log.Println(err) - } - } -} func createSshKeyPair() { publicKey := viper.GetString("botpublickey") @@ -1060,47 +258,4 @@ configs: } } -func unzip(zipFilepath string, unzipDirectory string) { - dst := unzipDirectory - archive, err := zip.OpenReader(zipFilepath) - if err != nil { - panic(err) - } - defer archive.Close() - - for _, f := range archive.File { - filePath := filepath.Join(dst, f.Name) - log.Println("unzipping file ", filePath) - - if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) { - log.Println("invalid file path") - return - } - if f.FileInfo().IsDir() { - log.Println("creating directory...") - os.MkdirAll(filePath, os.ModePerm) - continue - } - - if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - panic(err) - } - - dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - panic(err) - } - - fileInArchive, err := f.Open() - if err != nil { - panic(err) - } - - if _, err := io.Copy(dstFile, fileInArchive); err != nil { - panic(err) - } - dstFile.Close() - fileInArchive.Close() - } -} diff --git a/cmd/installationSteps.go b/cmd/installationSteps.go deleted file mode 100644 index dc83388a3..000000000 --- a/cmd/installationSteps.go +++ /dev/null @@ -1,225 +0,0 @@ -package cmd - -import ( - "fmt" - "log" - "os" - "strings" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "os/exec" - "syscall" - "time" - - "net/url" - "net/http" - "encoding/json" - - "github.com/google/uuid" - "bytes" - "encoding/base64" -) - -func applyBaseTerraform(cmd *cobra.Command,directory string){ - applyBase := viper.GetBool("create.terraformapplied.base") - if applyBase != true { - log.Println("Executing ApplyBaseTerraform") - if dryrunMode { - log.Printf("[#99] Dry-run mode, applyBaseTerraform skipped.") - return - } - terraformAction := "apply" - - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) - - err := os.Chdir(directory) - if err != nil { - log.Panicf("error changing dir") - } - - viperDestoryFlag := viper.GetBool("terraform.destroy") - cmdDestroyFlag, _ := cmd.Flags().GetBool("destroy") - - if viperDestoryFlag == true || cmdDestroyFlag == true { - terraformAction = "destroy" - } - - log.Println("terraform action: ", terraformAction, "destroyFlag: ", viperDestoryFlag) - execShellReturnStrings(terraformPath, "init") - execShellReturnStrings(terraformPath, fmt.Sprintf("%s", terraformAction), "-auto-approve") - keyOut, _, errKey := execShellReturnStrings(terraformPath, "output", "vault_unseal_kms_key") - if errKey != nil { - log.Panicf("failed to call tfOutputCmd.Run(): ", err) - } - keyId := strings.TrimSpace(keyOut) - log.Println("keyid is:", keyId) - viper.Set("vault.kmskeyid", keyId) - viper.Set("create.terraformapplied.base", true) - viper.WriteConfig() - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) - } else { - log.Println("Skipping: ApplyBaseTerraform") - } -} - - -func applyGitlabTerraform(directory string){ - if !viper.GetBool("create.terraformapplied.gitlab") { - log.Println("Executing applyGitlabTerraform") - if dryrunMode { - log.Printf("[#99] Dry-run mode, applyGitlabTerraform skipped.") - return - } - // Prepare for terraform gitlab execution - os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) - os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) - - directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) - err := os.Chdir(directory) - if err != nil { - log.Println("error changing dir") - } - execShellReturnStrings(terraformPath, "init") - execShellReturnStrings(terraformPath, "apply", "-auto-approve") - viper.Set("create.terraformapplied.gitlab", true) - viper.WriteConfig() - } else { - log.Println("Skipping: applyGitlabTerraform") - } -} - -func configureSoftserveAndPush(){ - configureAndPushFlag := viper.GetBool("create.softserve.configure") - if configureAndPushFlag != true { - log.Println("Executing configureSoftserveAndPush") - if dryrunMode { - log.Printf("[#99] Dry-run mode, configureSoftserveAndPush skipped.") - return - } - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Println("failed to call kPortForward.Run(): ", err) - } - time.Sleep(10 * time.Second) - - configureSoftServe() - pushGitopsToSoftServe() - viper.Set("create.softserve.configure", true) - viper.WriteConfig() - time.Sleep(10 * time.Second) - } else { - log.Println("Skipping: configureSoftserveAndPush") - } -} - -func gitlabKeyUpload(){ - // upload ssh public key - if !viper.GetBool("gitlab.keyuploaded") { - log.Println("Executing gitlabKeyUpload") - if dryrunMode { - log.Printf("[#99] Dry-run mode, gitlabKeyUpload skipped.") - return - } - log.Println("uploading ssh public key to gitlab") - gitlabToken := viper.GetString("gitlab.token") - data := url.Values{ - "title": {"kubefirst"}, - "key": {viper.GetString("botpublickey")}, - } - - gitlabUrlBase := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) - - resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) - if err != nil { - log.Fatal(err) - } - var res map[string]interface{} - json.NewDecoder(resp.Body).Decode(&res) - log.Println(res) - log.Println("ssh public key uploaded to gitlab") - viper.Set("gitlab.keyuploaded", true) - viper.WriteConfig() - } else { - log.Println("Skipping: gitlabKeyUpload") - log.Println("ssh public key already uploaded to gitlab") - } -} - - -func produceGitlabTokens(){ - //TODO: Should this step be skipped if already executed? - log.Println("discovering gitlab toolbox pod") - if dryrunMode { - log.Printf("[#99] Dry-run mode, produceGitlabTokens skipped.") - return - } - var outb, errb bytes.Buffer - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pod", "-lapp=toolbox", "-o", "jsonpath='{.items[0].metadata.name}'") - k.Stdout = &outb - k.Stderr = &errb - err := k.Run() - if err != nil { - log.Println("failed to call k.Run() to get gitlab pod: ", err) - } - gitlabPodName := outb.String() - gitlabPodName = strings.Replace(gitlabPodName, "'", "", -1) - log.Println("gitlab pod", gitlabPodName) - - gitlabToken := viper.GetString("gitlab.token") - if gitlabToken == "" { - - log.Println("getting gitlab personal access token") - - id := uuid.New() - gitlabToken = id.String()[:20] - - k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err = k.Run() - if err != nil { - log.Println("failed to call k.Run() to set gitlab token: ", err) - } - - viper.Set("gitlab.token", gitlabToken) - viper.WriteConfig() - - log.Println("gitlabToken", gitlabToken) - } - - gitlabRunnerToken := viper.GetString("gitlab.runnertoken") - if gitlabRunnerToken == "" { - - log.Println("getting gitlab runner token") - - var tokenOut, tokenErr bytes.Buffer - k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "secret", "gitlab-gitlab-runner-secret", "-o", "jsonpath='{.data.runner-registration-token}'") - k.Stdout = &tokenOut - k.Stderr = &tokenErr - err = k.Run() - if err != nil { - log.Println("failed to call k.Run() to get gitlabRunnerRegistrationToken: ", err) - } - encodedToken := tokenOut.String() - log.Println(encodedToken) - encodedToken = strings.Replace(encodedToken, "'", "", -1) - log.Println(encodedToken) - gitlabRunnerRegistrationTokenBytes, err := base64.StdEncoding.DecodeString(encodedToken) - gitlabRunnerRegistrationToken := string(gitlabRunnerRegistrationTokenBytes) - log.Println(gitlabRunnerRegistrationToken) - if err != nil { - panic(err) - } - viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) - viper.WriteConfig() - log.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) - } - -} \ No newline at end of file diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go new file mode 100644 index 000000000..8f8b08f21 --- /dev/null +++ b/cmd/kubefirstTemplate.go @@ -0,0 +1,169 @@ +package cmd + +import ( + "fmt" + "log" + "os" + ssh2 "golang.org/x/crypto/ssh" + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + "path/filepath" + "strings" + "github.com/spf13/viper" + "io/ioutil" + "time" + + +) + +func cloneGitOpsRepo() { + + url := "https://github.com/kubefirst/gitops-template" + directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + + // Clone the given repository to the given directory + log.Println("git clone", url, directory) + + _, err := git.PlainClone(directory, false, &git.CloneOptions{ + URL: url, + }) + if err != nil { + log.Println(err) + } + + println("downloaded gitops repo from template to directory", home, "/.kubefirst/gitops") +} + +func pushGitopsToSoftServe() { + + directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + + // // Clone the given repository to the given directory + log.Println("open %s git repo", directory) + + repo, err := git.PlainOpen(directory) + if err != nil { + log.Println("error opening the directory ", directory, err) + } + + log.Println("git remote add origin ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops") + _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "soft", + URLs: []string{"ssh://127.0.0.1:8022/gitops"}, + }) + if err != nil { + log.Println("Error creating remote repo:", err) + os.Exit(1) + } + w, _ := repo.Worktree() + + log.Println("Committing new changes...") + w.Add(".") + w.Commit("setting new remote upstream to soft-serve", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: installerEmail, + When: time.Now(), + }, + }) + + auth, _ := publicKey() + + auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + err = repo.Push(&git.PushOptions{ + RemoteName: "soft", + Auth: auth, + }) + if err != nil { + log.Println("error pushing to remote", err) + } + +} + + +func detokenize(path string) { + + err := filepath.Walk(path, detokenizeDirectory) + if err != nil { + panic(err) + } +} + +func detokenizeDirectory(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if fi.IsDir() { + return nil // + } + + if strings.Contains(path, ".git") || strings.Contains(path, ".terraform") { + return nil + } + + matched, err := filepath.Match("*", fi.Name()) + + if err != nil { + panic(err) + } + + if matched { + read, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + // todo should detokenize be a switch statement based on a value found in viper? + gitlabConfigured := viper.GetBool("gitlab.keyuploaded") + + newContents := "" + + if gitlabConfigured { + newContents = strings.Replace(string(read), "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.domainname")), -1) + } else { + newContents = strings.Replace(string(read), "https://gitlab./kubefirst/gitops.git", "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", -1) + } + + botPublicKey := viper.GetString("botpublickey") + domainId := viper.GetString("aws.domainid") + domainName := viper.GetString("aws.domainname") + bucketStateStore := viper.GetString("bucket.state-store.name") + bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") + bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") + bucketChartmuseum := viper.GetString("bucket.chartmuseum.name") + region := viper.GetString("aws.region") + adminEmail := viper.GetString("adminemail") + awsAccountId := viper.GetString("aws.accountid") + kmsKeyId := viper.GetString("vault.kmskeyid") + + newContents = strings.Replace(newContents, "", strings.TrimSpace(botPublicKey), -1) + newContents = strings.Replace(newContents, "", bucketStateStore, -1) + newContents = strings.Replace(newContents, "", bucketArgoArtifacts, -1) + newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) + newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) + newContents = strings.Replace(newContents, "", domainId, -1) + newContents = strings.Replace(newContents, "", domainName, -1) + newContents = strings.Replace(newContents, "", region, -1) + newContents = strings.Replace(newContents, "", adminEmail, -1) + newContents = strings.Replace(newContents, "", awsAccountId, -1) + if kmsKeyId != "" { + newContents = strings.Replace(newContents, "", kmsKeyId, -1) + } + + if viper.GetBool("create.terraformapplied.gitlab") { + newContents = strings.Replace(newContents, "", domainName, -1) + newContents = strings.Replace(newContents, "", region, -1) + newContents = strings.Replace(newContents, "", awsAccountId, -1) + } + + err = ioutil.WriteFile(path, []byte(newContents), 0) + if err != nil { + panic(err) + } + + } + + return nil +} diff --git a/cmd/kubectl.go b/cmd/kubernetes.go similarity index 100% rename from cmd/kubectl.go rename to cmd/kubernetes.go diff --git a/cmd/shell.go b/cmd/shell.go new file mode 100644 index 000000000..f1d1316d2 --- /dev/null +++ b/cmd/shell.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "log" + "bytes" + "os/exec" +) + +func execShellReturnStrings(command string, args ...string) (string, string, error) { + var outb, errb bytes.Buffer + k := exec.Command(command, args...) + k.Stdout = &outb + k.Stderr = &errb + err := k.Run() + if err != nil { + log.Println("Error executing command: %v", err) + } + return outb.String(), errb.String(), err +} diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go new file mode 100644 index 000000000..fdf84bb37 --- /dev/null +++ b/cmd/stepsArgo.go @@ -0,0 +1,403 @@ +package cmd + +import ( + "fmt" + "log" + "os" + "strings" + "github.com/spf13/viper" + + "os/exec" + "time" + + "net/url" + "net/http" + "encoding/json" + + "github.com/google/uuid" + "bytes" + "encoding/base64" + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" + b64 "encoding/base64" + v1 "k8s.io/api/core/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "html/template" + "github.com/ghodss/yaml" + "context" +) + +func helmInstallArgocd(home string, kubeconfigPath string) { + argocdCreated := viper.GetBool("create.argocd.helm") + if !argocdCreated { + if dryrunMode { + log.Printf("[#99] Dry-run mode, helmInstallArgocd skipped.") + return + } + helmClientPath := fmt.Sprintf("%s/.kubefirst/tools/helm", home) + + // ! commenting out until a clean execution is necessary // create namespace + helmRepoAddArgocd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") + helmRepoAddArgocd.Stdout = os.Stdout + helmRepoAddArgocd.Stderr = os.Stderr + err := helmRepoAddArgocd.Run() + if err != nil { + log.Println("failed to call helmRepoAddArgocd.Run(): %v", err) + } + + helmRepoUpdate := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "update") + helmRepoUpdate.Stdout = os.Stdout + helmRepoUpdate.Stderr = os.Stderr + err = helmRepoUpdate.Run() + if err != nil { + log.Println("failed to call helmRepoUpdate.Run(): %v", err) + } + + helmInstallArgocdOut, helmInstallArgocdErr,errHelmInstallArgocd := execShellReturnStrings(helmClientPath, "--kubeconfig", kubeconfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") + log.Printf("Result:\n\t%s\n\t%s\n",helmInstallArgocdOut,helmInstallArgocdErr) + if errHelmInstallArgocd != nil { + log.Println("failed to call helmInstallArgocdCmd.Run(): %v", err) + } + + viper.Set("create.argocd.helm", true) + err = viper.WriteConfig() + if err != nil { + log.Println(err) + } + } +} + +func awaitGitlab() { + if dryrunMode { + log.Printf("[#99] Dry-run mode, awaitGitlab skipped.") + return + } + log.Println("awaitGitlab called") + max := 200 + for i := 0; i < max; i++ { + + // todo should this be aws.hostedzonedname since we're sticking to an + // todo aws: and gcp: figure their nomenclature is more familar + hostedZoneName := viper.GetString("aws.domainname") + + resp, _ := http.Get(fmt.Sprintf("https://gitlab.%s", hostedZoneName)) + if resp != nil && resp.StatusCode == 200 { + log.Println("gitlab host resolved, 30 second grace period required...") + time.Sleep(time.Second * 30) + i = max + } else { + log.Println("gitlab host not resolved, sleeping 10s") + time.Sleep(time.Second * 10) + } + } +} + +func produceGitlabTokens(){ + //TODO: Should this step be skipped if already executed? + log.Println("discovering gitlab toolbox pod") + if dryrunMode { + log.Printf("[#99] Dry-run mode, produceGitlabTokens skipped.") + return + } + var outb, errb bytes.Buffer + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pod", "-lapp=toolbox", "-o", "jsonpath='{.items[0].metadata.name}'") + k.Stdout = &outb + k.Stderr = &errb + err := k.Run() + if err != nil { + log.Println("failed to call k.Run() to get gitlab pod: ", err) + } + gitlabPodName := outb.String() + gitlabPodName = strings.Replace(gitlabPodName, "'", "", -1) + log.Println("gitlab pod", gitlabPodName) + + gitlabToken := viper.GetString("gitlab.token") + if gitlabToken == "" { + + log.Println("getting gitlab personal access token") + + id := uuid.New() + gitlabToken = id.String()[:20] + + k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + log.Println("failed to call k.Run() to set gitlab token: ", err) + } + + viper.Set("gitlab.token", gitlabToken) + viper.WriteConfig() + + log.Println("gitlabToken", gitlabToken) + } + + gitlabRunnerToken := viper.GetString("gitlab.runnertoken") + if gitlabRunnerToken == "" { + + log.Println("getting gitlab runner token") + + var tokenOut, tokenErr bytes.Buffer + k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "secret", "gitlab-gitlab-runner-secret", "-o", "jsonpath='{.data.runner-registration-token}'") + k.Stdout = &tokenOut + k.Stderr = &tokenErr + err = k.Run() + if err != nil { + log.Println("failed to call k.Run() to get gitlabRunnerRegistrationToken: ", err) + } + encodedToken := tokenOut.String() + log.Println(encodedToken) + encodedToken = strings.Replace(encodedToken, "'", "", -1) + log.Println(encodedToken) + gitlabRunnerRegistrationTokenBytes, err := base64.StdEncoding.DecodeString(encodedToken) + gitlabRunnerRegistrationToken := string(gitlabRunnerRegistrationTokenBytes) + log.Println(gitlabRunnerRegistrationToken) + if err != nil { + panic(err) + } + viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) + viper.WriteConfig() + log.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) + } + +} + +func applyGitlabTerraform(directory string){ + if !viper.GetBool("create.terraformapplied.gitlab") { + log.Println("Executing applyGitlabTerraform") + if dryrunMode { + log.Printf("[#99] Dry-run mode, applyGitlabTerraform skipped.") + return + } + // Prepare for terraform gitlab execution + os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) + os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) + + directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) + err := os.Chdir(directory) + if err != nil { + log.Println("error changing dir") + } + execShellReturnStrings(terraformPath, "init") + execShellReturnStrings(terraformPath, "apply", "-auto-approve") + viper.Set("create.terraformapplied.gitlab", true) + viper.WriteConfig() + } else { + log.Println("Skipping: applyGitlabTerraform") + } +} + + + +func gitlabKeyUpload(){ + // upload ssh public key + if !viper.GetBool("gitlab.keyuploaded") { + log.Println("Executing gitlabKeyUpload") + if dryrunMode { + log.Printf("[#99] Dry-run mode, gitlabKeyUpload skipped.") + return + } + log.Println("uploading ssh public key to gitlab") + gitlabToken := viper.GetString("gitlab.token") + data := url.Values{ + "title": {"kubefirst"}, + "key": {viper.GetString("botpublickey")}, + } + + gitlabUrlBase := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) + + resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) + if err != nil { + log.Fatal(err) + } + var res map[string]interface{} + json.NewDecoder(resp.Body).Decode(&res) + log.Println(res) + log.Println("ssh public key uploaded to gitlab") + viper.Set("gitlab.keyuploaded", true) + viper.WriteConfig() + } else { + log.Println("Skipping: gitlabKeyUpload") + log.Println("ssh public key already uploaded to gitlab") + } +} + +func pushGitopsToGitLab() { + if dryrunMode { + log.Printf("[#99] Dry-run mode, pushGitopsToGitLab skipped.") + return + } + + //TODO: should this step to be skipped if already executed? + domain := viper.GetString("aws.domainname") + + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) + directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + + repo, err := git.PlainOpen(directory) + if err != nil { + log.Println("error opening the directory ", directory, err) + } + + //upstream := fmt.Sprintf("ssh://gitlab.%s:22:kubefirst/gitops", viper.GetString("aws.domainname")) + // upstream := "git@gitlab.kube1st.com:kubefirst/gitops.git" + upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) + log.Println("git remote add gitlab at url", upstream) + + _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "gitlab", + URLs: []string{upstream}, + }) + if err != nil { + log.Println("Error creating remote repo:", err) + } + w, _ := repo.Worktree() + + os.RemoveAll(directory + "/terraform/base/.terraform") + os.RemoveAll(directory + "/terraform/gitlab/.terraform") + os.RemoveAll(directory + "/terraform/vault/.terraform") + + log.Println("Committing new changes...") + w.Add(".") + _, err = w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: installerEmail, + When: time.Now(), + }, + }) + if err != nil { + log.Println("error committing changes", err) + } + + log.Println("setting auth...") + // auth, _ := publicKey() + // auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + auth := &gitHttp.BasicAuth{ + Username: "root", + Password: viper.GetString("gitlab.token"), + } + + err = repo.Push(&git.PushOptions{ + RemoteName: "gitlab", + Auth: auth, + }) + if err != nil { + log.Println("error pushing to remote", err) + } + +} + +func changeRegistryToGitLab() { + if !viper.GetBool("gitlab.registry") { + if dryrunMode { + log.Printf("[#99] Dry-run mode, changeRegistryToGitLab skipped.") + return + } + + type ArgocdGitCreds struct { + PersonalAccessToken string + URL string + FullURL string + } + + pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) + url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.domainname")))) + fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.domainname")))) + + creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} + + var argocdRepositoryAccessTokenSecret *v1.Secret + kubeconfig := home + "/.kubefirst/gitops/terraform/base/kubeconfig_kubefirst" + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + panic(err.Error()) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) + } + argocdSecretClient = clientset.CoreV1().Secrets("argocd") + + var secrets bytes.Buffer + + c, err := template.New("creds-gitlab").Parse(` + apiVersion: v1 + data: + password: {{ .PersonalAccessToken }} + url: {{ .URL }} + username: cm9vdA== + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repo-creds + name: creds-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&secrets, creds); err != nil { + log.Panic(err) + } + log.Println(secrets.String()) + + ba := []byte(secrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + + _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + panic(err) + } + + var repoSecrets bytes.Buffer + + c, err = template.New("repo-gitlab").Parse(` + apiVersion: v1 + data: + project: ZGVmYXVsdA== + type: Z2l0 + url: {{ .FullURL }} + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repository + name: repo-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&repoSecrets, creds); err != nil { + log.Panic(err) + } + log.Println(repoSecrets.String()) + + ba = []byte(repoSecrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + + _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + panic(err) + } + + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", home)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + log.Println("failed to call k.Run() to apply argocd patch to adopt gitlab: ", err) + } + + viper.Set("gitlab.registry", true) + viper.WriteConfig() + } else { + log.Println("Skipping: changeRegistryToGitLab") + } +} \ No newline at end of file diff --git a/cmd/stepsBaseInstall.go b/cmd/stepsBaseInstall.go new file mode 100644 index 000000000..f3726074b --- /dev/null +++ b/cmd/stepsBaseInstall.go @@ -0,0 +1,54 @@ +package cmd + +import ( + "fmt" + "log" + "os" + "strings" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func applyBaseTerraform(cmd *cobra.Command,directory string){ + applyBase := viper.GetBool("create.terraformapplied.base") + if applyBase != true { + log.Println("Executing ApplyBaseTerraform") + if dryrunMode { + log.Printf("[#99] Dry-run mode, applyBaseTerraform skipped.") + return + } + terraformAction := "apply" + + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) + + err := os.Chdir(directory) + if err != nil { + log.Panicf("error changing dir") + } + + viperDestoryFlag := viper.GetBool("terraform.destroy") + cmdDestroyFlag, _ := cmd.Flags().GetBool("destroy") + + if viperDestoryFlag == true || cmdDestroyFlag == true { + terraformAction = "destroy" + } + + log.Println("terraform action: ", terraformAction, "destroyFlag: ", viperDestoryFlag) + execShellReturnStrings(terraformPath, "init") + execShellReturnStrings(terraformPath, fmt.Sprintf("%s", terraformAction), "-auto-approve") + keyOut, _, errKey := execShellReturnStrings(terraformPath, "output", "vault_unseal_kms_key") + if errKey != nil { + log.Panicf("failed to call tfOutputCmd.Run(): ", err) + } + keyId := strings.TrimSpace(keyOut) + log.Println("keyid is:", keyId) + viper.Set("vault.kmskeyid", keyId) + viper.Set("create.terraformapplied.base", true) + viper.WriteConfig() + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) + } else { + log.Println("Skipping: ApplyBaseTerraform") + } +} \ No newline at end of file diff --git a/cmd/stepsMetaphor.go b/cmd/stepsMetaphor.go new file mode 100644 index 000000000..2c16c6051 --- /dev/null +++ b/cmd/stepsMetaphor.go @@ -0,0 +1,64 @@ +package cmd + +import ( + "fmt" + "log" + "github.com/spf13/viper" + "time" + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" +) + +func hydrateGitlabMetaphorRepo() { + //TODO: Should this be skipped if already executed? + if dryrunMode { + log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") + return + } + metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", home) + + url := "https://github.com/kubefirst/metaphor-template" + + metaphorTemplateRepo, err := git.PlainClone(metaphorTemplateDir, false, &git.CloneOptions{ + URL: url, + }) + if err != nil { + panic("error cloning metaphor-template repo") + } + + detokenize(metaphorTemplateDir) + + // todo make global + domainName := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) + log.Println("git remote add origin", domainName) + _, err = metaphorTemplateRepo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "gitlab", + URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.git", domainName)}, + }) + + w, _ := metaphorTemplateRepo.Worktree() + + log.Println("Committing new changes...") + w.Add(".") + w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: installerEmail, + When: time.Now(), + }, + }) + + err = metaphorTemplateRepo.Push(&git.PushOptions{ + RemoteName: "gitlab", + Auth: &gitHttp.BasicAuth{ + Username: "root", + Password: viper.GetString("gitlab.token"), + }, + }) + if err != nil { + log.Println("error pushing to remote", err) + } + +} \ No newline at end of file diff --git a/cmd/stepsSoftServe.go b/cmd/stepsSoftServe.go new file mode 100644 index 000000000..dc9c13c37 --- /dev/null +++ b/cmd/stepsSoftServe.go @@ -0,0 +1,136 @@ +package cmd + +import ( + "fmt" + "log" + "os" + "strings" + "github.com/spf13/viper" + "os/exec" + "syscall" + "time" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" + ssh2 "golang.org/x/crypto/ssh" + "io/ioutil" +) + +func createSoftServe(kubeconfigPath string) { + createSoftServeFlag := viper.GetBool("create.softserve.create") + if createSoftServeFlag != true { + log.Println("Executing createSoftServe") + if dryrunMode { + log.Printf("[#99] Dry-run mode, createSoftServe skipped.") + return + } + toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) + + err := os.Mkdir(toolsDir, 0777) + if err != nil { + log.Println("error creating directory %s", toolsDir, err) + } + + // create soft-serve stateful set + softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", home) + softServeApplyOut, softServeApplyErr,errSoftServeApply := execShellReturnStrings(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") + log.Printf("Result:\n\t%s\n\t%s\n",softServeApplyOut,softServeApplyErr) + if errSoftServeApply != nil { + log.Println("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) + } + + viper.Set("create.softserve.create", true) + viper.WriteConfig() + log.Println("waiting for soft-serve installation to complete...") + time.Sleep(60 * time.Second) + //TODO: Update mechanism of waiting + } else { + log.Println("Skipping: createSoftServe") + } + +} + +func configureSoftserveAndPush(){ + configureAndPushFlag := viper.GetBool("create.softserve.configure") + if configureAndPushFlag != true { + log.Println("Executing configureSoftserveAndPush") + if dryrunMode { + log.Printf("[#99] Dry-run mode, configureSoftserveAndPush skipped.") + return + } + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Println("failed to call kPortForward.Run(): ", err) + } + time.Sleep(10 * time.Second) + + configureSoftServe() + pushGitopsToSoftServe() + viper.Set("create.softserve.configure", true) + viper.WriteConfig() + time.Sleep(10 * time.Second) + } else { + log.Println("Skipping: configureSoftserveAndPush") + } +} + +func configureSoftServe() { + // todo clone repo + // todo manipulate config.yaml + // todo git add / commit / push + url := "ssh://127.0.0.1:8022/config" + directory := fmt.Sprintf("%s/.kubefirst/config", home) + + // Clone the given repository to the given directory + log.Println("git clone", url, directory) + + auth, _ := publicKey() + + auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + repo, err := git.PlainClone(directory, false, &git.CloneOptions{ + URL: url, + Auth: auth, + }) + if err != nil { + log.Println("error!, ", err) + } + + file, err := ioutil.ReadFile(fmt.Sprintf("%s/config.yaml", directory)) + if err != nil { + log.Println("error reading file", err) + } + + newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) + + err = ioutil.WriteFile(fmt.Sprintf("%s/config.yaml", directory), []byte(newFile), 0) + if err != nil { + panic(err) + } + + println("re-wrote config.yaml", home, "/.kubefirst/config") + + w, _ := repo.Worktree() + + log.Println("Committing new changes...") + w.Add(".") + w.Commit("updating soft-serve server config", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: installerEmail, + When: time.Now(), + }, + }) + + err = repo.Push(&git.PushOptions{ + RemoteName: "origin", + Auth: auth, + }) + if err != nil { + log.Println("error pushing to remote", err) + } + +} diff --git a/cmd/stepsUndefined.go b/cmd/stepsUndefined.go new file mode 100644 index 000000000..464b6cf1a --- /dev/null +++ b/cmd/stepsUndefined.go @@ -0,0 +1,36 @@ +package cmd + + +import ( + "log" + "strings" + "github.com/spf13/viper" + "io/ioutil" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" +) + +func modConfigYaml() { + + file, err := ioutil.ReadFile("./config.yaml") + if err != nil { + log.Println("error reading file", err) + } + + newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) + + err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) + if err != nil { + panic(err) + } +} + + + +func publicKey() (*ssh.PublicKeys, error) { + var publicKey *ssh.PublicKeys + publicKey, err := ssh.NewPublicKeys("git", []byte(viper.GetString("botprivatekey")), "") + if err != nil { + return nil, err + } + return publicKey, err +} \ No newline at end of file diff --git a/cmd/stepsVault.go b/cmd/stepsVault.go new file mode 100644 index 000000000..697c95255 --- /dev/null +++ b/cmd/stepsVault.go @@ -0,0 +1,202 @@ +package cmd + +import ( + "fmt" + "log" + "os" + "strings" + "github.com/spf13/viper" + "os/exec" + "encoding/json" + "bytes" + "encoding/base64" + gitlab "github.com/xanzy/go-gitlab" + vault "github.com/hashicorp/vault/api" +) + + +func configureVault() { + if !viper.GetBool("create.terraformapplied.vault") { + if dryrunMode { + log.Printf("[#99] Dry-run mode, configureVault skipped.") + return + } + // ``` + // NOTE: the terraform here produces unnecessary $var.varname vars in the atlantis secret for nonsensitive values + // the following atlantis secrets shouldn't have vars in the gitops source code for the atlantis secret, they + // should look like us-east-1, in flat string code as non-sensitive vals - refactor soon. + // "TF_VAR_aws_region": "us-east-1", + // "TF_VAR_aws_account_id": "${var.aws_account_id}", + // "TF_VAR_email_address": "${var.email_address}", + // "TF_VAR_hosted_zone_id": "${var.hosted_zone_id}", + // "TF_VAR_hosted_zone_name": "${var.hosted_zone_name}", + // "TF_VAR_vault_addr": "${var.vault_addr}", + // ``` + // ... obviously keep the sensitive values bound to vars + + //TODO replace this command: + var outb, errb bytes.Buffer + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "get", "secret", "vault-unseal-keys", "-o", "jsonpath='{.data.cluster-keys\\.json}'") + k.Stdout = &outb + k.Stderr = &errb + err := k.Run() + if err != nil { + log.Println("failed to call k.Run() to get gitlab pod: ", err) + } + vaultKeysEncoded := outb.String() + vaultKeysEncoded = strings.Replace(vaultKeysEncoded, "'", "", -1) + log.Println("vault keys", vaultKeysEncoded) + + vaultKeysBytes, err := base64.StdEncoding.DecodeString(vaultKeysEncoded) + log.Println(vaultKeysBytes) + if err != nil { + panic(err) + } + vaultKeys := string(vaultKeysBytes) + log.Println(vaultKeys) + + var dat map[string]interface{} + if err := json.Unmarshal([]byte(vaultKeys), &dat); err != nil { + panic(err) + } + vaultToken := dat["root_token"].(string) + log.Println(vaultToken) + viper.Set("vault.token", vaultToken) + viper.WriteConfig() + + // Prepare for terraform vault execution + os.Setenv("VAULT_ADDR", fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname"))) + os.Setenv("VAULT_TOKEN", vaultToken) + + os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname"))) + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_email_address", viper.GetString("adminemail")) + os.Setenv("TF_VAR_gitlab_runner_token", viper.GetString("gitlab.runnertoken")) + os.Setenv("TF_VAR_gitlab_token", viper.GetString("gitlab.token")) + os.Setenv("TF_VAR_hosted_zone_id", viper.GetString("aws.domainid")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) + os.Setenv("TF_VAR_vault_token", viper.GetString("aws.domainname")) + os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") + + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", home) + err = os.Chdir(directory) + if err != nil { + log.Println("error changing dir") + } + + tfInitCmd := exec.Command(terraformPath, "init") + tfInitCmd.Stdout = os.Stdout + tfInitCmd.Stderr = os.Stderr + err = tfInitCmd.Run() + if err != nil { + log.Println("failed to call vault terraform init: ", err) + } + + tfApplyCmd := exec.Command(terraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") + tfApplyCmd.Stdout = os.Stdout + tfApplyCmd.Stderr = os.Stderr + err = tfApplyCmd.Run() + if err != nil { + log.Println("failed to call vault terraform apply: ", err) + } + + viper.Set("create.terraformapplied.vault", true) + viper.WriteConfig() + } else { + log.Println("Skipping: configureVault") + } +} + +func addGitlabOidcApplications() { + //TODO: Should this skipped if already executed. + if dryrunMode { + log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") + return + } + domain := viper.GetString("aws.domainname") + git, err := gitlab.NewClient( + viper.GetString("gitlab.token"), + gitlab.WithBaseURL(fmt.Sprintf("https://gitlab.%s/api/v4", domain)), + ) + if err != nil { + log.Fatal(err) + } + + apps := []string{"argo", "argocd", "vault"} + cb := make(map[string]string) + cb["argo"] = fmt.Sprintf("https://argo.%s/oauth2/callback", domain) + cb["argocd"] = fmt.Sprintf("https://argocd.%s/auth/callback", domain) + cb["vault"] = fmt.Sprintf("https://vault.%s:8250/oidc/callback http://localhost:8250/oidc/callback https://vault.%s/ui/vault/auth/oidc/oidc/callback http://localhost:8200/ui/vault/auth/oidc/oidc/callback", domain, domain) + + for _, app := range apps { + log.Println("checking to see if", app, "oidc application needs to be created in gitlab") + appId := viper.GetString(fmt.Sprintf("gitlab.oidc.%s.applicationid", app)) + if appId == "" { + + // Create an application + opts := &gitlab.CreateApplicationOptions{ + Name: gitlab.String(app), + RedirectURI: gitlab.String(cb[app]), + Scopes: gitlab.String("read_user openid email"), + } + createdApp, _, err := git.Applications.CreateApplication(opts) + if err != nil { + log.Fatal(err) + } + + // List all applications + existingApps, _, err := git.Applications.ListApplications(&gitlab.ListApplicationsOptions{}) + if err != nil { + log.Fatal(err) + } + + created := false + for _, existingApp := range existingApps { + if existingApp.ApplicationName == app { + created = true + } + } + if created { + log.Println("created gitlab oidc application with applicationid", createdApp.ApplicationID) + viper.Set(fmt.Sprintf("gitlab.oidc.%s.applicationid", app), createdApp.ApplicationID) + viper.Set(fmt.Sprintf("gitlab.oidc.%s.secret", app), createdApp.Secret) + + secretData := map[string]interface{}{ + "data": map[string]interface{}{ + "application_id": createdApp.ApplicationID, + "secret": createdApp.Secret, + }, + } + secretPath := fmt.Sprintf("secret/data/oidc/%s", app) + addVaultSecret(secretPath, secretData) + viper.WriteConfig() + } else { + log.Panic("could not create gitlab iodc application", app) + } + } + } +} + +func addVaultSecret(secretPath string, secretData map[string]interface{}) { + log.Println("vault called") + + config := vault.DefaultConfig() + + config.Address = fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname")) + + client, err := vault.NewClient(config) + if err != nil { + log.Println("unable to initialize Vault client: ", err) + } + + client.SetToken(viper.GetString("vault.token")) + + // Writing a secret + _, err = client.Logical().Write(secretPath, secretData) + if err != nil { + log.Println("unable to write secret: ", err) + } else { + log.Println("secret written successfully.") + } +} \ No newline at end of file From 5c077bce108d1df4ce84b8cb8584756811af62bd Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Mon, 4 Jul 2022 18:57:24 +0000 Subject: [PATCH 023/107] reorg funcs Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/versionConstant.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/versionConstant.go b/cmd/versionConstant.go index 581dcf4bf..5feef682a 100644 --- a/cmd/versionConstant.go +++ b/cmd/versionConstant.go @@ -2,4 +2,4 @@ package cmd //This file may be updated/generated by CI/CD to always produce final and unique versions //allowing easier to track source of bugs. -const NebolousVersion = "0.0.1" \ No newline at end of file +const NebolousVersion = "0.1.1" \ No newline at end of file From e29a749e7a5d1638768ca22aed6789108ef00298 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 11:53:13 +0000 Subject: [PATCH 024/107] synching changes Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 9 +++ cmd/destroy.go | 92 +++++++++++++++++++----------- cmd/stepsArgo.go | 132 +++++++++++++++++++++++++++++++++---------- cmd/stepsMetaphor.go | 89 ++++++++++++++++------------- cmd/stepsVault.go | 70 ++++++++++------------- 5 files changed, 249 insertions(+), 143 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 52f822fd0..2a9c2e225 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -77,6 +77,15 @@ to quickly create a Cobra application.`, Trackers[trackerStage23].Tracker.Increment(int64(1)) hydrateGitlabMetaphorRepo() Trackers[trackerStage23].Tracker.Increment(int64(1)) + + token := getArgocdAuthToken() + syncArgocdApplication("argo-components", token) + syncArgocdApplication("gitlab-runner-components", token) + syncArgocdApplication("gitlab-runner", token) + syncArgocdApplication("atlantis-components", token) + syncArgocdApplication("chartmuseum-components", token) + + metricName = "kubefirst.mgmt_cluster_install.completed" if !dryrunMode { diff --git a/cmd/destroy.go b/cmd/destroy.go index 12fee7b86..2d34f6546 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -9,6 +9,8 @@ import ( "log" "os" "os/exec" + "syscall" + "time" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -17,18 +19,25 @@ import ( // destroyCmd represents the destroy command var destroyCmd = &cobra.Command{ Use: "destroy", - Short: "A brief description of your command", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: + Short: "destroy the kubefirst management cluster", + Long: `destory the kubefirst management cluster +and all of the components in kubernetes. -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, +Optional: skip gitlab terraform +if the registry has already been delteted.`, Run: func(cmd *cobra.Command, args []string) { + + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to gitlab %s", err) + } // todo this needs to be removed when we are no longer in the starter account - os.Setenv("AWS_PROFILE", "starter") - log.Println("TODO -- need to setup and argocd delete against registry and wait?") + log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") // kubeconfig := os.Getenv("HOME") + "/.kube/config" // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) // argocdclientset, err := argocdclientset.NewForConfig(config) @@ -36,26 +45,27 @@ to quickly create a Cobra application.`, // return nil, err // } - // todo should we git clone the gitops repo when destroy is run back to their local host to get the latest values of gitops ? + //* should we git clone the gitops repo when destroy is run back to their + //* local host to get the latest values of gitops os.Setenv("AWS_REGION", viper.GetString("aws.region")) os.Setenv("AWS_ACCOUNT_ID", viper.GetString("aws.accountid")) - os.Setenv("HOSTED_ZONE_NAME", viper.GetString("aws.domainname")) + os.Setenv("HOSTED_ZONE_NAME", viper.GetString("aws.hostedzonename")) os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - skipGitlabTerraform, _ := cmd.Flags().GetBool("skip-gitlab-terraform") - //! terraform destroy gitlab directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) - err := os.Chdir(directory) + skipGitlabTerraform, _ := cmd.Flags().GetBool("skip-gitlab-terraform") + + err = os.Chdir(directory) if err != nil { - log.Println("error changing dir: ", directory) + log.Panicf("error: could not change directory to " + directory) } - os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) + os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") if !skipGitlabTerraform { tfInitGitlabCmd := exec.Command(terraformPath, "init") @@ -63,7 +73,7 @@ to quickly create a Cobra application.`, tfInitGitlabCmd.Stderr = os.Stderr err = tfInitGitlabCmd.Run() if err != nil { - log.Panicf("failed to call terraform init gitlab: ", err) + log.Panicf("failed to terraform init gitlab %s", err) } tfDestroyGitlabCmd := exec.Command(terraformPath, "destroy", "-auto-approve") @@ -71,26 +81,30 @@ to quickly create a Cobra application.`, tfDestroyGitlabCmd.Stderr = os.Stderr err = tfDestroyGitlabCmd.Run() if err != nil { - log.Panicf("failed to call terraform destroy gitlab: ", err) + log.Panicf("failed to terraform destroy gitlab %s", err) } viper.Set("destroy.terraformdestroy.gitlab", true) viper.WriteConfig() } - //! terraform destroy base directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) err = os.Chdir(directory) if err != nil { - log.Println("error changing dir: ", directory) + log.Panicf("error: could not change directory to " + directory) } + // delete argocd registry + deleteRegistryApplication() + log.Println("sleeping for 42 seconds to allow the registry to delete") + time.Sleep(42 * time.Second) + tfInitBaseCmd := exec.Command(terraformPath, "init") tfInitBaseCmd.Stdout = os.Stdout tfInitBaseCmd.Stderr = os.Stderr err = tfInitBaseCmd.Run() if err != nil { - log.Println("failed to call terraform init base: ", err) + log.Panicf("failed to terraform init base %s", err) } tfDestroyBaseCmd := exec.Command(terraformPath, "destroy", "-auto-approve") @@ -98,8 +112,7 @@ to quickly create a Cobra application.`, tfDestroyBaseCmd.Stderr = os.Stderr err = tfDestroyBaseCmd.Run() if err != nil { - log.Println("failed to call terraform destroy base: ", err) - panic("failed to terraform destroy base") + log.Panicf("failed to terraform destroy base %s", err) } viper.Set("destroy.terraformdestroy.base", true) @@ -108,17 +121,30 @@ to quickly create a Cobra application.`, } func init() { - rootCmd.AddCommand(destroyCmd) + nebulousCmd.AddCommand(destroyCmd) destroyCmd.Flags().Bool("skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") +} - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // destroyCmd.PersistentFlags().String("foo", "", "A help for foo") - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // destroyCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} +func deleteRegistryApplication() { + log.Println("starting port forward to argocd server and deleting registry") + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd %s", err) + } + + url := "https://localhost:8080/api/v1/applications/registry" + argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) + argoCdAppSync.Stdout = os.Stdout + argoCdAppSync.Stderr = os.Stderr + err = argoCdAppSync.Run() + if err != nil { + log.Panicf("error: curl appSync failed failed %s", err) + } + log.Println("deleting argocd application registry") +} \ No newline at end of file diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index fdf84bb37..e1eb98539 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -32,21 +32,18 @@ import ( ) func helmInstallArgocd(home string, kubeconfigPath string) { - argocdCreated := viper.GetBool("create.argocd.helm") - if !argocdCreated { + if !viper.GetBool("create.argocd.helm") { if dryrunMode { log.Printf("[#99] Dry-run mode, helmInstallArgocd skipped.") return } - helmClientPath := fmt.Sprintf("%s/.kubefirst/tools/helm", home) - // ! commenting out until a clean execution is necessary // create namespace helmRepoAddArgocd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") helmRepoAddArgocd.Stdout = os.Stdout helmRepoAddArgocd.Stderr = os.Stderr err := helmRepoAddArgocd.Run() if err != nil { - log.Println("failed to call helmRepoAddArgocd.Run(): %v", err) + log.Panicf("error: could not run helm repo add %s", err) } helmRepoUpdate := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "update") @@ -54,36 +51,34 @@ func helmInstallArgocd(home string, kubeconfigPath string) { helmRepoUpdate.Stderr = os.Stderr err = helmRepoUpdate.Run() if err != nil { - log.Println("failed to call helmRepoUpdate.Run(): %v", err) + log.Panicf("error: could not helm repo update %s", err) } - helmInstallArgocdOut, helmInstallArgocdErr,errHelmInstallArgocd := execShellReturnStrings(helmClientPath, "--kubeconfig", kubeconfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") - log.Printf("Result:\n\t%s\n\t%s\n",helmInstallArgocdOut,helmInstallArgocdErr) - if errHelmInstallArgocd != nil { - log.Println("failed to call helmInstallArgocdCmd.Run(): %v", err) + helmInstallArgocdCmd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") + helmInstallArgocdCmd.Stdout = os.Stdout + helmInstallArgocdCmd.Stderr = os.Stderr + err = helmInstallArgocdCmd.Run() + if err != nil { + log.Panicf("error: could not helm install argocd command %s", err) } viper.Set("create.argocd.helm", true) err = viper.WriteConfig() if err != nil { - log.Println(err) + log.Panicf("error: could not write to viper config") } } } func awaitGitlab() { + log.Println("awaitGitlab called") if dryrunMode { log.Printf("[#99] Dry-run mode, awaitGitlab skipped.") return - } - log.Println("awaitGitlab called") + } max := 200 for i := 0; i < max; i++ { - - // todo should this be aws.hostedzonedname since we're sticking to an - // todo aws: and gcp: figure their nomenclature is more familar - hostedZoneName := viper.GetString("aws.domainname") - + hostedZoneName := viper.GetString("aws.hostedzonename") resp, _ := http.Get(fmt.Sprintf("https://gitlab.%s", hostedZoneName)) if resp != nil && resp.StatusCode == 200 { log.Println("gitlab host resolved, 30 second grace period required...") @@ -308,20 +303,19 @@ func changeRegistryToGitLab() { } pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) - url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.domainname")))) - fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.domainname")))) + url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.hostedzonename")))) + fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")))) creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} var argocdRepositoryAccessTokenSecret *v1.Secret - kubeconfig := home + "/.kubefirst/gitops/terraform/base/kubeconfig_kubefirst" - config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) if err != nil { - panic(err.Error()) + log.Panicf("error getting client from kubeconfig") } clientset, err := kubernetes.NewForConfig(config) if err != nil { - panic(err.Error()) + log.Panicf("error getting kubeconfig for clientset") } argocdSecretClient = clientset.CoreV1().Secrets("argocd") @@ -344,16 +338,15 @@ func changeRegistryToGitLab() { type: Opaque `) if err := c.Execute(&secrets, creds); err != nil { - log.Panic(err) + log.Panicf("error executing golang template for git repository credentials template %s", err) } - log.Println(secrets.String()) ba := []byte(secrets.String()) err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) if err != nil { - panic(err) + log.Panicf("error creating argocd repository credentials template secret %s", err) } var repoSecrets bytes.Buffer @@ -375,16 +368,15 @@ func changeRegistryToGitLab() { type: Opaque `) if err := c.Execute(&repoSecrets, creds); err != nil { - log.Panic(err) + log.Panicf("error executing golang template for gitops repository template %s", err) } - log.Println(repoSecrets.String()) ba = []byte(repoSecrets.String()) err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) if err != nil { - panic(err) + log.Panicf("error creating argocd repository connection secret %s", err) } k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", home)) @@ -392,7 +384,7 @@ func changeRegistryToGitLab() { k.Stderr = os.Stderr err = k.Run() if err != nil { - log.Println("failed to call k.Run() to apply argocd patch to adopt gitlab: ", err) + log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) } viper.Set("gitlab.registry", true) @@ -400,4 +392,82 @@ func changeRegistryToGitLab() { } else { log.Println("Skipping: changeRegistryToGitLab") } +} + +func getArgocdAuthToken() string { + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd %s", err) + } + + url := "https://localhost:8080/api/v1/session" + + payload := strings.NewReader(fmt.Sprintf("{\n\t\"username\":\"admin\",\"password\":\"%s\"\n}", viper.GetString("argocd.admin.password"))) + + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + req, err := http.NewRequest("POST", url, payload) + if err != nil { + log.Fatal("error getting auth token from argocd ", err) + } + + client := &http.Client{ + CheckRedirect: func(req *http.Request, via []*http.Request) error { + // N.B.: when used in production, also check for redirect loops + return nil + }, + } + + res, err := client.Do(req) + if err != nil { + log.Fatal("error requesting auth token from argocd") + } + + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Fatal("error sending POST request to get argocd auth token :", err) + } + + var dat map[string]interface{} + + if err := json.Unmarshal(body, &dat); err != nil { + log.Panicf("error unmarshalling %s", err) + } + token := dat["token"] + viper.Set("argocd.admin.apitoken", token) + viper.WriteConfig() + + return token.(string) + +} + +func syncArgocdApplication(applicationName, argocdAuthToken string) { + if dryrunMode { + log.Printf("[#99] Dry-run mode, syncArgocdApplication skipped.") + return + } + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd %s", err)) + } + + // todo need to replace this with a curl wrapper and see if it WORKS + + url := fmt.Sprintf("https://localhost:8080/api/v1/applications/%s/sync", applicationName) + + argoCdAppSync := exec.Command("curl", "-k", "-L", "-X", "POST", url, "-H", fmt.Sprintf("Authorization: Bearer %s", argocdAuthToken)) + argoCdAppSync.Stdout = os.Stdout + argoCdAppSync.Stderr = os.Stderr + err = argoCdAppSync.Run() + if err != nil { + log.Panicf("error: curl appSync failed failed %s", err)) + } } \ No newline at end of file diff --git a/cmd/stepsMetaphor.go b/cmd/stepsMetaphor.go index 2c16c6051..95c4b705e 100644 --- a/cmd/stepsMetaphor.go +++ b/cmd/stepsMetaphor.go @@ -13,52 +13,63 @@ import ( func hydrateGitlabMetaphorRepo() { //TODO: Should this be skipped if already executed? - if dryrunMode { - log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") - return - } - metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", home) + if !viper.GetBool("create.gitlabmetaphor.cloned") { + if dryrunMode { + log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") + return + } - url := "https://github.com/kubefirst/metaphor-template" + metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", home) - metaphorTemplateRepo, err := git.PlainClone(metaphorTemplateDir, false, &git.CloneOptions{ - URL: url, - }) - if err != nil { - panic("error cloning metaphor-template repo") - } + url := "https://github.com/kubefirst/metaphor-template" + + metaphorTemplateRepo, err := git.PlainClone(metaphorTemplateDir, false, &git.CloneOptions{ + URL: url, + }) + if err != nil { + log.Panicf("error cloning metaphor-template repo") + } + viper.Set("create.gitlabmetaphor.cloned", true) + + detokenize(metaphorTemplateDir) + + viper.Set("create.gitlabmetaphor.detokenized", true) - detokenize(metaphorTemplateDir) + // todo make global + gitlabURL := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.hostedzonename")) + log.Println("git remote add origin", gitlabURL) + _, err = metaphorTemplateRepo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "gitlab", + URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.git", gitlabURL)}, + }) - // todo make global - domainName := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) - log.Println("git remote add origin", domainName) - _, err = metaphorTemplateRepo.CreateRemote(&gitConfig.RemoteConfig{ - Name: "gitlab", - URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.git", domainName)}, - }) + w, _ := metaphorTemplateRepo.Worktree() - w, _ := metaphorTemplateRepo.Worktree() + log.Println("Committing detokenized metaphor content") + w.Add(".") + w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: "kubefirst-bot@kubefirst.com", + When: time.Now(), + }, + }) - log.Println("Committing new changes...") - w.Add(".") - w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ - Author: &object.Signature{ - Name: "kubefirst-bot", - Email: installerEmail, - When: time.Now(), - }, - }) + err = metaphorTemplateRepo.Push(&git.PushOptions{ + RemoteName: "gitlab", + Auth: &gitHttp.BasicAuth{ + Username: "root", + Password: viper.GetString("gitlab.token"), + }, + }) + if err != nil { + log.Panicf("error pushing detokenized metaphor repository to remote at" + gitlabURL) + } - err = metaphorTemplateRepo.Push(&git.PushOptions{ - RemoteName: "gitlab", - Auth: &gitHttp.BasicAuth{ - Username: "root", - Password: viper.GetString("gitlab.token"), - }, - }) - if err != nil { - log.Println("error pushing to remote", err) + viper.Set("create.gitlabmetaphor.pushed", true) + viper.WriteConfig() + } else { + log.Println("Skipping: hydrateGitlabMetaphorRepo") } } \ No newline at end of file diff --git a/cmd/stepsVault.go b/cmd/stepsVault.go index 697c95255..b681ee448 100644 --- a/cmd/stepsVault.go +++ b/cmd/stepsVault.go @@ -34,55 +34,49 @@ func configureVault() { // ``` // ... obviously keep the sensitive values bound to vars - //TODO replace this command: - var outb, errb bytes.Buffer - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "get", "secret", "vault-unseal-keys", "-o", "jsonpath='{.data.cluster-keys\\.json}'") - k.Stdout = &outb - k.Stderr = &errb - err := k.Run() + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) if err != nil { - log.Println("failed to call k.Run() to get gitlab pod: ", err) + log.Panicf("error: getting config %s", err) } - vaultKeysEncoded := outb.String() - vaultKeysEncoded = strings.Replace(vaultKeysEncoded, "'", "", -1) - log.Println("vault keys", vaultKeysEncoded) - - vaultKeysBytes, err := base64.StdEncoding.DecodeString(vaultKeysEncoded) - log.Println(vaultKeysBytes) + clientset, err := kubernetes.NewForConfig(config) if err != nil { - panic(err) + log.Panicf("error: getting config &s", err) } - vaultKeys := string(vaultKeysBytes) - log.Println(vaultKeys) - var dat map[string]interface{} - if err := json.Unmarshal([]byte(vaultKeys), &dat); err != nil { - panic(err) - } - vaultToken := dat["root_token"].(string) - log.Println(vaultToken) + vaultSecretClient = clientset.CoreV1().Secrets("vault") + vaultToken := kubefirst.GetVaultRootToken(vaultSecretClient) + viper.Set("vault.token", vaultToken) viper.WriteConfig() + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err = kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to vault namespce svc/vault %s", err) + } + // Prepare for terraform vault execution - os.Setenv("VAULT_ADDR", fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname"))) + os.Setenv("VAULT_ADDR", "http://localhost:8200") os.Setenv("VAULT_TOKEN", vaultToken) - os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname"))) + os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename"))) os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) os.Setenv("TF_VAR_email_address", viper.GetString("adminemail")) os.Setenv("TF_VAR_gitlab_runner_token", viper.GetString("gitlab.runnertoken")) os.Setenv("TF_VAR_gitlab_token", viper.GetString("gitlab.token")) os.Setenv("TF_VAR_hosted_zone_id", viper.GetString("aws.domainid")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) - os.Setenv("TF_VAR_vault_token", viper.GetString("aws.domainname")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) + os.Setenv("TF_VAR_vault_token", viper.GetString("aws.hostedzonename")) os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", home) err = os.Chdir(directory) if err != nil { - log.Println("error changing dir") + log.Panicf("error: could not change directory to " + directory) } tfInitCmd := exec.Command(terraformPath, "init") @@ -90,7 +84,7 @@ func configureVault() { tfInitCmd.Stderr = os.Stderr err = tfInitCmd.Run() if err != nil { - log.Println("failed to call vault terraform init: ", err) + log.Panicf("error: terraform init failed %s", err) } tfApplyCmd := exec.Command(terraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") @@ -98,7 +92,7 @@ func configureVault() { tfApplyCmd.Stderr = os.Stderr err = tfApplyCmd.Run() if err != nil { - log.Println("failed to call vault terraform apply: ", err) + log.Panicf("error: terraform apply failed %s", err) } viper.Set("create.terraformapplied.vault", true) @@ -114,7 +108,7 @@ func addGitlabOidcApplications() { log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") return } - domain := viper.GetString("aws.domainname") + domain := viper.GetString("aws.hostedzonename") git, err := gitlab.NewClient( viper.GetString("gitlab.token"), gitlab.WithBaseURL(fmt.Sprintf("https://gitlab.%s/api/v4", domain)), @@ -148,7 +142,7 @@ func addGitlabOidcApplications() { // List all applications existingApps, _, err := git.Applications.ListApplications(&gitlab.ListApplicationsOptions{}) if err != nil { - log.Fatal(err) + log.Panicf("error: could not list applications from gitlab") } created := false @@ -172,31 +166,27 @@ func addGitlabOidcApplications() { addVaultSecret(secretPath, secretData) viper.WriteConfig() } else { - log.Panic("could not create gitlab iodc application", app) + log.Panicf("could not create gitlab oidc application %s", app) } } } } func addVaultSecret(secretPath string, secretData map[string]interface{}) { - log.Println("vault called") - config := vault.DefaultConfig() - - config.Address = fmt.Sprintf("https://vault.%s", viper.GetString("aws.domainname")) + config.Address = fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename")) client, err := vault.NewClient(config) if err != nil { - log.Println("unable to initialize Vault client: ", err) + log.Panicf("unable to initialize vault client %s", err) } client.SetToken(viper.GetString("vault.token")) - // Writing a secret _, err = client.Logical().Write(secretPath, secretData) if err != nil { - log.Println("unable to write secret: ", err) + log.Panicf("unable to write secret vault secret %s - error: %s", secretPath, err) } else { - log.Println("secret written successfully.") + log.Println("secret successfully written to path: ", secretPath) } } \ No newline at end of file From f749ebbdd24eb6422b1749426500065258733ee6 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 12:09:29 +0000 Subject: [PATCH 025/107] synching changes Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/gitlab.go | 65 ++++++++++++++++++++++++++++++++++++++++ cmd/init.go | 7 ++--- cmd/kubefirstTemplate.go | 5 ++-- cmd/stepsArgo.go | 4 +-- cmd/stepsSoftServe.go | 15 ++++------ 5 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 cmd/gitlab.go diff --git a/cmd/gitlab.go b/cmd/gitlab.go new file mode 100644 index 000000000..b9a453728 --- /dev/null +++ b/cmd/gitlab.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "log" + "net/http" + "net/url" + "os" + "os/exec" + "syscall" + + "github.com/google/uuid" + "github.com/spf13/viper" +) + +func gitlabGeneratePersonalAccessToken(gitlabPodName string) { + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to gitlab %s", err) + } + + log.Println("generating gitlab personal access token on pod: ", gitlabPodName) + + id := uuid.New() + gitlabToken := id.String()[:20] + + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + log.Panicf("error running exec against %s to generate gitlab personal access token for root user", gitlabPodName) + } + + viper.Set("gitlab.token", gitlabToken) + viper.WriteConfig() + + log.Println("gitlab personal access token generated", gitlabToken) +} + +func uploadGitlabSSHKey(gitlabToken string) { + data := url.Values{ + "title": {"kubefirst"}, + "key": {viper.GetString("botpublickey")}, + } + + gitlabUrlBase := fmt.Sprintf("http://localhost:8888") + + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) + if err != nil { + log.Panicf("error: failed to upload ssh key to gitlab") + } + var res map[string]interface{} + json.NewDecoder(resp.Body).Decode(&res) + log.Println("ssh public key uploaded to gitlab") + viper.Set("gitlab.keyuploaded", true) + viper.WriteConfig() +} \ No newline at end of file diff --git a/cmd/init.go b/cmd/init.go index 91777954e..8540f6449 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -206,7 +206,7 @@ func createSshKeyPair() { viper.Set("botPrivateKey", privateKey) err := viper.WriteConfig() if err != nil { - log.Println(err) + log.Panicf("error: could not write to viper config") } } publicKey = viper.GetString("botpublickey") @@ -249,12 +249,9 @@ configs: %s `, strings.ReplaceAll(privateKey, "\n", "\n "))) - // fmt.Println("argo init vals:\n", string(argocdInitValuesYaml)) - err := ioutil.WriteFile(fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), argocdInitValuesYaml, 0644) if err != nil { - log.Println("received an error while writing the argocd-init-values.yaml file", err.Error()) - panic("error: argocd-init-values.yaml" + err.Error()) + log.Panicf("error: could not write argocd-init-values.yaml %s", err) } } diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 8f8b08f21..f10cef41f 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -22,17 +22,16 @@ func cloneGitOpsRepo() { url := "https://github.com/kubefirst/gitops-template" directory := fmt.Sprintf("%s/.kubefirst/gitops", home) - // Clone the given repository to the given directory log.Println("git clone", url, directory) _, err := git.PlainClone(directory, false, &git.CloneOptions{ URL: url, }) if err != nil { - log.Println(err) + log.Panicf("reror cloning gitops-template repository from github %s", err) } - println("downloaded gitops repo from template to directory", home, "/.kubefirst/gitops") + log.Println("downloaded gitops repo from template to directory", home, "/.kubefirst/gitops") } func pushGitopsToSoftServe() { diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index e1eb98539..43086f246 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -456,7 +456,7 @@ func syncArgocdApplication(applicationName, argocdAuthToken string) { err := kPortForward.Start() defer kPortForward.Process.Signal(syscall.SIGTERM) if err != nil { - log.Panicf("error: failed to port-forward to argocd %s", err)) + log.Panicf("error: failed to port-forward to argocd %s", err) } // todo need to replace this with a curl wrapper and see if it WORKS @@ -468,6 +468,6 @@ func syncArgocdApplication(applicationName, argocdAuthToken string) { argoCdAppSync.Stderr = os.Stderr err = argoCdAppSync.Run() if err != nil { - log.Panicf("error: curl appSync failed failed %s", err)) + log.Panicf("error: curl appSync failed failed %s", err) } } \ No newline at end of file diff --git a/cmd/stepsSoftServe.go b/cmd/stepsSoftServe.go index dc9c13c37..e82acabb2 100644 --- a/cmd/stepsSoftServe.go +++ b/cmd/stepsSoftServe.go @@ -16,8 +16,7 @@ import ( ) func createSoftServe(kubeconfigPath string) { - createSoftServeFlag := viper.GetBool("create.softserve.create") - if createSoftServeFlag != true { + if !viper.GetBool("create.softserve.create") { log.Println("Executing createSoftServe") if dryrunMode { log.Printf("[#99] Dry-run mode, createSoftServe skipped.") @@ -35,7 +34,7 @@ func createSoftServe(kubeconfigPath string) { softServeApplyOut, softServeApplyErr,errSoftServeApply := execShellReturnStrings(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") log.Printf("Result:\n\t%s\n\t%s\n",softServeApplyOut,softServeApplyErr) if errSoftServeApply != nil { - log.Println("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) + log.Panicf("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) } viper.Set("create.softserve.create", true) @@ -78,13 +77,9 @@ func configureSoftserveAndPush(){ } func configureSoftServe() { - // todo clone repo - // todo manipulate config.yaml - // todo git add / commit / push url := "ssh://127.0.0.1:8022/config" directory := fmt.Sprintf("%s/.kubefirst/config", home) - // Clone the given repository to the given directory log.Println("git clone", url, directory) auth, _ := publicKey() @@ -96,12 +91,12 @@ func configureSoftServe() { Auth: auth, }) if err != nil { - log.Println("error!, ", err) + log.Panicf("error cloning config repository from soft serve") } file, err := ioutil.ReadFile(fmt.Sprintf("%s/config.yaml", directory)) if err != nil { - log.Println("error reading file", err) + log.Panicf("error reading config.yaml file %s", err) } newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) @@ -130,7 +125,7 @@ func configureSoftServe() { Auth: auth, }) if err != nil { - log.Println("error pushing to remote", err) + llog.Panicf("error pushing to remote", err) } } From 59774ac86b0d3b2020bf1a47540ca722bd1550d7 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 12:24:54 +0000 Subject: [PATCH 026/107] synching changes Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/aws.go | 7 +++---- cmd/files.go | 16 ++++++++-------- cmd/kubefirstTemplate.go | 20 +++++++++----------- cmd/stepsArgo.go | 12 ++++++------ 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/cmd/aws.go b/cmd/aws.go index b599d1143..4a3e5b978 100644 --- a/cmd/aws.go +++ b/cmd/aws.go @@ -77,13 +77,12 @@ func bucketRand() { func getAccountInfo() { cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { - log.Println("failed to load configuration, error:", err) + log.Panicf("failed to load configuration, error: %s", err) } - // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration stsClient := sts.NewFromConfig(cfg) iamCaller, err := stsClient.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{}) if err != nil { - log.Println("oh no error on call", err) + log.Panicf("error: could not get caller identity %s", err) } viper.Set("aws.accountid", *iamCaller.Account) @@ -238,7 +237,7 @@ func getDNSInfo(hostedZoneName string) string { if *zone.Name == fmt.Sprintf(`%s%s`, hostedZoneName, ".") { zoneId = returnHostedZoneId(*zone.Id) log.Printf(`found entry for user submitted domain %s, using hosted zone id %s`, hostedZoneName, zoneId) - viper.Set("aws.domainname", hostedZoneName) + viper.Set("aws.hostedzonename", hostedZoneName) viper.Set("aws.domainid", zoneId) viper.WriteConfig() } diff --git a/cmd/files.go b/cmd/files.go index 81ce2a4b0..50a1e4621 100644 --- a/cmd/files.go +++ b/cmd/files.go @@ -33,7 +33,7 @@ func download() { kubectlStdOut, kubectlStdErr,errKubectl := execShellReturnStrings(kubectlClientPath, "version", "--client", "--short") log.Printf("-> kubectl version:\n\t%s\n\t%s\n",kubectlStdOut,kubectlStdErr) if errKubectl != nil { - log.Println("failed to call kubectlVersionCmd.Run(): %v", err) + log.Panicf("failed to call kubectlVersionCmd.Run(): %v", err) } Trackers[trackerStage5].Tracker.Increment(int64(1)) @@ -59,7 +59,7 @@ func download() { downloadFile(terraformDownloadZipPath, terraformDownloadUrl) // terraformZipDownload, err := os.Open(terraformDownloadZipPath) if err != nil { - log.Println("error reading terraform file") + log.Panicf("error reading terraform file") } unzipDirectory := fmt.Sprintf("%s/.kubefirst/tools", home) unzip(terraformDownloadZipPath, unzipDirectory) @@ -75,7 +75,7 @@ func download() { downloadFile(helmDownloadTarGzPath, helmDownloadUrl) helmTarDownload, err := os.Open(helmDownloadTarGzPath) if err != nil { - log.Println("error reading helm file") + log.Panicf("could not read helm download content") } extractFileFromTarGz(helmTarDownload, fmt.Sprintf("%s-%s/helm", localOs, localArchitecture), helmClientPath) os.Chmod(helmClientPath, 0755) @@ -84,7 +84,7 @@ func download() { // currently argocd init values is generated by flare nebulous ssh // todo helm install argocd --create-namespace --wait --values ~/.kubefirst/argocd-init-values.yaml argo/argo-cd if errHelm != nil { - log.Println("failed to call helmVersionCmd.Run(): %v", err) + log.Panicf("error executing helm version command: %v", err) } Trackers[trackerStage5].Tracker.Increment(int64(1)) @@ -121,7 +121,7 @@ func downloadFile(filepath string, url string) (err error) { func extractFileFromTarGz(gzipStream io.Reader, tarAddress string, targetFilePath string) { uncompressedStream, err := gzip.NewReader(gzipStream) if err != nil { - log.Fatal("extractTarGz: NewReader failed") + log.Panicf("extractTarGz: NewReader failed") } tarReader := tar.NewReader(uncompressedStream) @@ -132,7 +132,7 @@ func extractFileFromTarGz(gzipStream io.Reader, tarAddress string, targetFilePat break } if err != nil { - log.Println("extractTarGz: Next() failed: %s", err.Error()) + log.Panicf("extractTarGz: Next() failed: %s", err.Error()) } log.Println(header.Name) if header.Name == tarAddress { @@ -140,10 +140,10 @@ func extractFileFromTarGz(gzipStream io.Reader, tarAddress string, targetFilePat case tar.TypeReg: outFile, err := os.Create(targetFilePath) if err != nil { - log.Println("extractTarGz: Create() failed: %s", err.Error()) + log.Panicf("extractTarGz: Create() failed: %s", err.Error()) } if _, err := io.Copy(outFile, tarReader); err != nil { - log.Println("extractTarGz: Copy() failed: %s", err.Error()) + log.Panicf("extractTarGz: Copy() failed: %s", err.Error()) } outFile.Close() diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index f10cef41f..9fad85e0f 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -38,12 +38,11 @@ func pushGitopsToSoftServe() { directory := fmt.Sprintf("%s/.kubefirst/gitops", home) - // // Clone the given repository to the given directory - log.Println("open %s git repo", directory) + log.Println("open git repo", directory) repo, err := git.PlainOpen(directory) if err != nil { - log.Println("error opening the directory ", directory, err) + log.Panicf("error opening the directory ", directory, err) } log.Println("git remote add origin ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops") @@ -52,8 +51,7 @@ func pushGitopsToSoftServe() { URLs: []string{"ssh://127.0.0.1:8022/gitops"}, }) if err != nil { - log.Println("Error creating remote repo:", err) - os.Exit(1) + log.Panicf("Error creating remote repo: %s", err) } w, _ := repo.Worktree() @@ -62,7 +60,7 @@ func pushGitopsToSoftServe() { w.Commit("setting new remote upstream to soft-serve", &git.CommitOptions{ Author: &object.Signature{ Name: "kubefirst-bot", - Email: installerEmail, + Email: "kubefirst-bot@kubefirst.com", When: time.Now(), }, }) @@ -76,7 +74,7 @@ func pushGitopsToSoftServe() { Auth: auth, }) if err != nil { - log.Println("error pushing to remote", err) + log.Panicf("error pushing to remote", err) } } @@ -120,14 +118,14 @@ func detokenizeDirectory(path string, fi os.FileInfo, err error) error { newContents := "" if gitlabConfigured { - newContents = strings.Replace(string(read), "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.domainname")), -1) + newContents = strings.Replace(string(read), "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")), -1) } else { newContents = strings.Replace(string(read), "https://gitlab./kubefirst/gitops.git", "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", -1) } botPublicKey := viper.GetString("botpublickey") domainId := viper.GetString("aws.domainid") - domainName := viper.GetString("aws.domainname") + hostedzonename := viper.GetString("aws.hostedzonename") bucketStateStore := viper.GetString("bucket.state-store.name") bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") @@ -143,7 +141,7 @@ func detokenizeDirectory(path string, fi os.FileInfo, err error) error { newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) newContents = strings.Replace(newContents, "", domainId, -1) - newContents = strings.Replace(newContents, "", domainName, -1) + newContents = strings.Replace(newContents, "", hostedzonename, -1) newContents = strings.Replace(newContents, "", region, -1) newContents = strings.Replace(newContents, "", adminEmail, -1) newContents = strings.Replace(newContents, "", awsAccountId, -1) @@ -152,7 +150,7 @@ func detokenizeDirectory(path string, fi os.FileInfo, err error) error { } if viper.GetBool("create.terraformapplied.gitlab") { - newContents = strings.Replace(newContents, "", domainName, -1) + newContents = strings.Replace(newContents, "", hostedzonename, -1) newContents = strings.Replace(newContents, "", region, -1) newContents = strings.Replace(newContents, "", awsAccountId, -1) } diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index 43086f246..505c6c9ee 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -229,17 +229,17 @@ func pushGitopsToGitLab() { } //TODO: should this step to be skipped if already executed? - domain := viper.GetString("aws.domainname") + domain := viper.GetString("aws.hostedzonename") detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) directory := fmt.Sprintf("%s/.kubefirst/gitops", home) repo, err := git.PlainOpen(directory) if err != nil { - log.Println("error opening the directory ", directory, err) + log.Panicf("error opening the directory ", directory, err) } - //upstream := fmt.Sprintf("ssh://gitlab.%s:22:kubefirst/gitops", viper.GetString("aws.domainname")) + //upstream := fmt.Sprintf("ssh://gitlab.%s:22:kubefirst/gitops", viper.GetString("aws.hostedzonename")) // upstream := "git@gitlab.kube1st.com:kubefirst/gitops.git" upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) log.Println("git remote add gitlab at url", upstream) @@ -262,12 +262,12 @@ func pushGitopsToGitLab() { _, err = w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ Author: &object.Signature{ Name: "kubefirst-bot", - Email: installerEmail, + Email: "kubefirst-bot@kubefirst.com", When: time.Now(), }, }) if err != nil { - log.Println("error committing changes", err) + log.Panicf("error committing changes", err) } log.Println("setting auth...") @@ -284,7 +284,7 @@ func pushGitopsToGitLab() { Auth: auth, }) if err != nil { - log.Println("error pushing to remote", err) + log.Panicf("error pushing to remote", err) } } From 233a1a3eae482a9d0fb2d97c6d127e457a0e43cc Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 12:33:46 +0000 Subject: [PATCH 027/107] synching changes Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/aws.go | 12 +++++------- cmd/kubernetes.go | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cmd/aws.go b/cmd/aws.go index 4a3e5b978..cb8de00d6 100644 --- a/cmd/aws.go +++ b/cmd/aws.go @@ -166,22 +166,20 @@ func testHostedZoneLiveness(hostedZoneName, hostedZoneId string) { log.Println(ips) if err != nil { - // tracker.Message = fmt.Sprintln("dns test", count, "of", 25) - fmt.Fprintf(os.Stderr, "Could not get record name %s - waiting 10 seconds and trying again: %v\n", route53RecordName, err) + log.Println(fmt.Sprintf("Could not get record name %s - waiting 10 seconds and trying again: \nerror: %s", route53RecordName, err)) time.Sleep(10 * time.Second) } else { for _, ip := range ips { // todo check ip against route53RecordValue in some capacity so we can pivot the value for testing - log.Printf("%s. in TXT record value: %s\n", route53RecordName, ip) + log.Println(fmt.Sprintf("%s. in TXT record value: %s\n", route53RecordName, ip)) //tracker.MarkAsDone() count = 26 } } if count == 25 { - log.Println("unable to resolve hosted zone dns record. please check your domain registrar") //tracker.MarkAsErrored() //pw.Stop() - os.Exit(1) + log.Panicf("unable to resolve hosted zone dns record. please check your domain registrar") } } // todo delete route53 record @@ -210,9 +208,9 @@ func testHostedZoneLiveness(hostedZoneName, hostedZoneId string) { // HostedZoneId: aws.String(hostedZoneId), // }) // if err != nil { - // fmt.Println("error deleting route 53 record after liveness test") + // log.Println("error deleting route 53 record after liveness test") // } - // fmt.Println("record deletion status is ", *&recordDelete.ChangeInfo.Status) + // log.Println("record deletion status is ", *&recordDelete.ChangeInfo.Status) } diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index c8e835113..1ee412758 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -26,7 +26,7 @@ var gitlabPodsClient coreV1Types.PodInterface func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) if err != nil { - log.Println(err) + fmt.Println(err) } gitlabToolboxPodName = pods.Items[0].Name From de5b6b8c0d3435d5e3fe6b345c6d52f6bc2e34f3 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:02:20 +0000 Subject: [PATCH 028/107] review create/init Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 22 +++++++--- cmd/init.go | 62 ++++++++------------------- cmd/stepsArgo.go | 94 ++++++++++++++++++++--------------------- cmd/stepsBaseInstall.go | 29 ++++++------- cmd/stepsSoftServe.go | 6 +-- 5 files changed, 95 insertions(+), 118 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 2a9c2e225..07c1f654a 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -59,22 +59,32 @@ to quickly create a Cobra application.`, Trackers[trackerStage21].Tracker.Increment(int64(1)) helmInstallArgocd(home, kubeconfigPath) Trackers[trackerStage22].Tracker.Increment(int64(1)) - awaitGitlab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) + produceGitlabTokens() Trackers[trackerStage22].Tracker.Increment(int64(1)) + + applyGitlabTerraform(directory) Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlabKeyUpload() Trackers[trackerStage22].Tracker.Increment(int64(1)) - pushGitopsToGitLab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - changeRegistryToGitLab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) + configureVault() Trackers[trackerStage23].Tracker.Increment(int64(1)) + + addGitlabOidcApplications() Trackers[trackerStage23].Tracker.Increment(int64(1)) + awaitGitlab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + + pushGitopsToGitLab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + changeRegistryToGitLab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + + + hydrateGitlabMetaphorRepo() Trackers[trackerStage23].Tracker.Increment(int64(1)) diff --git a/cmd/init.go b/cmd/init.go index 8540f6449..a9d56c093 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -55,33 +55,26 @@ to quickly create a Cobra application.`, Trackers[trackerStage8] = &flare.ActionTracker{flare.CreateTracker(trackerStage8, int64(1))} Trackers[trackerStage9] = &flare.ActionTracker{flare.CreateTracker(trackerStage9, int64(1))} infoCmd.Run(cmd, args) + hostedZoneName, _ := cmd.Flags().GetString("hosted-zone-name") metricName := "kubefirst.init.started" - metricDomain := "kubefirst.com" + metricDomain := hostedZoneName if !dryrunMode { flare.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } - // todo hack - awsProfileSet := os.Getenv("AWS_PROFILE") - - if awsProfileSet == "" { - log.Println("\nhack: !!!!! PLEASE SET AWS PROFILE !!!!!\n\nexport AWS_PROFILE=starter\n") - os.Exit(1) - } - + // todo need to check flags and create config // hosted zone name: // name of the hosted zone to be used for the kubefirst install // if suffixed with a dot (eg. kubefirst.com.), the dot will be stripped - hostedZoneName, _ := cmd.Flags().GetString("hosted-zone-name") if strings.HasSuffix(hostedZoneName, ".") { hostedZoneName = hostedZoneName[:len(hostedZoneName)-1] } log.Println("hostedZoneName:", hostedZoneName) - viper.Set("aws.domainname", hostedZoneName) + viper.Set("aws.hostedzonename", hostedZoneName) viper.WriteConfig() // admin email // used for letsencrypt notifications and the gitlab root account @@ -104,44 +97,40 @@ to quickly create a Cobra application.`, Trackers[trackerStage1].Tracker.Increment(int64(1)) //trackProgress(1, false) // todo: this doesn't default to testing the dns check - if !viper.GetBool("init.hostedzonecheck.enabled") { + skipHostedZoneCheck := viper.GetBool("init.hostedzonecheck.enabled") + if !skipHostedZoneCheck { log.Println("skipping hosted zone check") } else { testHostedZoneLiveness(hostedZoneName, hostedZoneId) } Trackers[trackerStage2].Tracker.Increment(int64(1)) - // todo generate ssh key --> ~/.kubefirst/ssh-key .pub - - //! step 1 - // todo rm -rf ~/.kubefirst - // todo make sure - k -n soft-serve port-forward svc/soft-serve 8022:22 log.Println("calling createSshKeyPair() ") createSshKeyPair() - log.Println("createSshKeyPair() complete\n\n") + log.Println("createSshKeyPair() complete") Trackers[trackerStage3].Tracker.Increment(int64(1)) - log.Println("calling cloneGitOpsRepo() function\n") + log.Println("calling cloneGitOpsRepo()") cloneGitOpsRepo() - log.Println("cloneGitOpsRepo() complete\n\n") + log.Println("cloneGitOpsRepo() complete") Trackers[trackerStage4].Tracker.Increment(int64(1)) - log.Println("calling download() ") + log.Println("calling download()") download() - log.Println("download() complete\n\n") + log.Println("download() complete") - log.Println("calling getAccountInfo() function\n") + log.Println("calling getAccountInfo()") getAccountInfo() - log.Println("getAccountInfo() complete\n\n") + log.Println("getAccountInfo() complete") Trackers[trackerStage6].Tracker.Increment(int64(1)) - log.Println("calling bucketRand() function\n") + log.Println("calling bucketRand()") bucketRand() - log.Println("bucketRand() complete\n\n") + log.Println("bucketRand() complete") - log.Println("calling detokenize() ") + log.Println("calling detokenize()") detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) - log.Println("detokenize() complete\n\n") + log.Println("detokenize() complete") Trackers[trackerStage8].Tracker.Increment(int64(1)) // modConfigYaml() @@ -170,7 +159,7 @@ func init() { initCmd.MarkFlagRequired("cloud") initCmd.Flags().String("region", "", "the region to provision the cloud resources in") initCmd.MarkFlagRequired("region") - initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.flare") + initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.flare") log.SetPrefix("LOG: ") log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) @@ -182,21 +171,6 @@ func init() { - - - - - - - - - - - - - - - func createSshKeyPair() { publicKey := viper.GetString("botpublickey") if publicKey == "" { diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index 505c6c9ee..90c625162 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -98,66 +98,53 @@ func produceGitlabTokens(){ log.Printf("[#99] Dry-run mode, produceGitlabTokens skipped.") return } - var outb, errb bytes.Buffer - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pod", "-lapp=toolbox", "-o", "jsonpath='{.items[0].metadata.name}'") - k.Stdout = &outb - k.Stderr = &errb - err := k.Run() - if err != nil { - log.Println("failed to call k.Run() to get gitlab pod: ", err) - } - gitlabPodName := outb.String() - gitlabPodName = strings.Replace(gitlabPodName, "'", "", -1) - log.Println("gitlab pod", gitlabPodName) + time.Sleep(30 * time.Second) + argocdSecretClient = clientset.CoreV1().Secrets("argocd") - gitlabToken := viper.GetString("gitlab.token") - if gitlabToken == "" { + argocdPassword := getSecretValue(argocdSecretClient, "argocd-initial-admin-secret", "password") - log.Println("getting gitlab personal access token") + viper.Set("argocd.admin.password", argocdPassword) + viper.WriteConfig() - id := uuid.New() - gitlabToken = id.String()[:20] + log.Println("discovering gitlab toolbox pod") - k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err = k.Run() - if err != nil { - log.Println("failed to call k.Run() to set gitlab token: ", err) + gitlabPodsClient = clientset.CoreV1().Pods("gitlab") + gitlabPodName := getPodNameByLabel(gitlabPodsClient, "toolbox") + + gitlabSecretClient = clientset.CoreV1().Secrets("gitlab") + secrets, err := gitlabSecretClient.List(context.TODO(), metaV1.ListOptions{}) + + var gitlabRootPasswordSecretName string + + for _, secret := range secrets.Items { + if strings.Contains(secret.Name, "initial-root-password") { + gitlabRootPasswordSecretName = secret.Name + log.Println("gitlab initial root password secret name: ", gitlabRootPasswordSecretName) } + } + gitlabRootPassword := getSecretValue(gitlabSecretClient, gitlabRootPasswordSecretName, "password") - viper.Set("gitlab.token", gitlabToken) - viper.WriteConfig() + viper.Set("gitlab.podname", gitlabPodName) + viper.Set("gitlab.root.password", gitlabRootPassword) + viper.WriteConfig() + + gitlabToken := viper.GetString("gitlab.token") + + if gitlabToken == "" { + + log.Println("generating gitlab personal access token") + gitlabGeneratePersonalAccessToken(gitlabPodName) - log.Println("gitlabToken", gitlabToken) } gitlabRunnerToken := viper.GetString("gitlab.runnertoken") + if gitlabRunnerToken == "" { log.Println("getting gitlab runner token") - - var tokenOut, tokenErr bytes.Buffer - k = exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "secret", "gitlab-gitlab-runner-secret", "-o", "jsonpath='{.data.runner-registration-token}'") - k.Stdout = &tokenOut - k.Stderr = &tokenErr - err = k.Run() - if err != nil { - log.Println("failed to call k.Run() to get gitlabRunnerRegistrationToken: ", err) - } - encodedToken := tokenOut.String() - log.Println(encodedToken) - encodedToken = strings.Replace(encodedToken, "'", "", -1) - log.Println(encodedToken) - gitlabRunnerRegistrationTokenBytes, err := base64.StdEncoding.DecodeString(encodedToken) - gitlabRunnerRegistrationToken := string(gitlabRunnerRegistrationTokenBytes) - log.Println(gitlabRunnerRegistrationToken) - if err != nil { - panic(err) - } + gitlabRunnerRegistrationToken := getSecretValue(gitlabSecretClient, "gitlab-gitlab-runner-secret", "runner-registration-token") viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) viper.WriteConfig() - log.Println("gitlabRunnerRegistrationToken", gitlabRunnerRegistrationToken) } } @@ -171,15 +158,23 @@ func applyGitlabTerraform(directory string){ } // Prepare for terraform gitlab execution os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) - os.Setenv("GITLAB_BASE_URL", fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname"))) + os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") + directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) err := os.Chdir(directory) if err != nil { - log.Println("error changing dir") + log.panic("error: could not change directory to " + directory) + } + _,_,errInit := execShellReturnStrings(terraformPath, "init") + if errInit != nil { + panic(fmt.Sprintf("error: terraform init for gitlab failed %s", err)) + } + _,_,errApply := execShellReturnStrings(terraformPath, "apply", "-auto-approve") + if errApply != nil { + panic(fmt.Sprintf("error: terraform apply for gitlab failed %s", err)) } - execShellReturnStrings(terraformPath, "init") - execShellReturnStrings(terraformPath, "apply", "-auto-approve") + os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) viper.Set("create.terraformapplied.gitlab", true) viper.WriteConfig() } else { @@ -193,6 +188,7 @@ func gitlabKeyUpload(){ // upload ssh public key if !viper.GetBool("gitlab.keyuploaded") { log.Println("Executing gitlabKeyUpload") + log.Println("uploading ssh public key for gitlab user") if dryrunMode { log.Printf("[#99] Dry-run mode, gitlabKeyUpload skipped.") return diff --git a/cmd/stepsBaseInstall.go b/cmd/stepsBaseInstall.go index f3726074b..d078cf844 100644 --- a/cmd/stepsBaseInstall.go +++ b/cmd/stepsBaseInstall.go @@ -17,32 +17,29 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ log.Printf("[#99] Dry-run mode, applyBaseTerraform skipped.") return } - terraformAction := "apply" - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.domainname")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) err := os.Chdir(directory) if err != nil { - log.Panicf("error changing dir") + log.Panicf("error, directory does not exist - did you `kubefirst init`?: %s \nerror: %s", directory, err) } - - viperDestoryFlag := viper.GetBool("terraform.destroy") - cmdDestroyFlag, _ := cmd.Flags().GetBool("destroy") - - if viperDestoryFlag == true || cmdDestroyFlag == true { - terraformAction = "destroy" + _,_,errInit := execShellReturnStrings(terraformPath, "init") + if errInit != nil { + panic(fmt.Sprintf("error: terraform init failed %s", err)) + } + _,_,errApply := execShellReturnStrings(terraformPath,"apply", "-auto-approve") + if errApply != nil { + panic(fmt.Sprintf("error: terraform init failed %s", err)) } - - log.Println("terraform action: ", terraformAction, "destroyFlag: ", viperDestoryFlag) - execShellReturnStrings(terraformPath, "init") - execShellReturnStrings(terraformPath, fmt.Sprintf("%s", terraformAction), "-auto-approve") keyOut, _, errKey := execShellReturnStrings(terraformPath, "output", "vault_unseal_kms_key") if errKey != nil { - log.Panicf("failed to call tfOutputCmd.Run(): ", err) + log.Panicf("error: terraform apply failed %s", err) } - keyId := strings.TrimSpace(keyOut) + os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) + keyIdNoSpace := strings.TrimSpace(keyOut) + keyId := keyIdNoSpace[1 : len(keyIdNoSpace)-1] log.Println("keyid is:", keyId) viper.Set("vault.kmskeyid", keyId) viper.Set("create.terraformapplied.base", true) diff --git a/cmd/stepsSoftServe.go b/cmd/stepsSoftServe.go index e82acabb2..72dfc38d0 100644 --- a/cmd/stepsSoftServe.go +++ b/cmd/stepsSoftServe.go @@ -62,15 +62,15 @@ func configureSoftserveAndPush(){ err := kPortForward.Start() defer kPortForward.Process.Signal(syscall.SIGTERM) if err != nil { - log.Println("failed to call kPortForward.Run(): ", err) + log.Panicf("error: failed to port-forward to soft-serve %s", err) } - time.Sleep(10 * time.Second) + time.Sleep(20 * time.Second) configureSoftServe() pushGitopsToSoftServe() viper.Set("create.softserve.configure", true) viper.WriteConfig() - time.Sleep(10 * time.Second) + time.Sleep(30 * time.Second) } else { log.Println("Skipping: configureSoftserveAndPush") } From 92d96325329bc68d0ce821ba472d3963f44f632e Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:15:47 +0000 Subject: [PATCH 029/107] review imports Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/init.go | 1 - cmd/stepsArgo.go | 18 ++++-- cmd/stepsSoftServe.go | 2 +- cmd/stepsVault.go | 8 +-- go.mod | 10 +--- go.sum | 7 +-- internal/argocd.go | 114 +++++++++++++++++++++++++++++++++++++ pkg/kubefirst/kubefirst.go | 48 ++++++++++++++++ 8 files changed, 183 insertions(+), 25 deletions(-) create mode 100644 internal/argocd.go create mode 100644 pkg/kubefirst/kubefirst.go diff --git a/cmd/init.go b/cmd/init.go index a9d56c093..434d43caf 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -8,7 +8,6 @@ import ( "fmt" "io/ioutil" "log" - "os" "strings" "time" "github.com/spf13/cobra" diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index 90c625162..003f85d31 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -6,17 +6,15 @@ import ( "os" "strings" "github.com/spf13/viper" - + "syscall" "os/exec" "time" - + "crypto/tls" "net/url" "net/http" "encoding/json" - - "github.com/google/uuid" + "io/ioutil" "bytes" - "encoding/base64" "github.com/go-git/go-git/v5" gitConfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" @@ -93,6 +91,14 @@ func awaitGitlab() { func produceGitlabTokens(){ //TODO: Should this step be skipped if already executed? + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + if err != nil { + panic(err.Error()) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) + } log.Println("discovering gitlab toolbox pod") if dryrunMode { log.Printf("[#99] Dry-run mode, produceGitlabTokens skipped.") @@ -164,7 +170,7 @@ func applyGitlabTerraform(directory string){ directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) err := os.Chdir(directory) if err != nil { - log.panic("error: could not change directory to " + directory) + log.Panic("error: could not change directory to " + directory) } _,_,errInit := execShellReturnStrings(terraformPath, "init") if errInit != nil { diff --git a/cmd/stepsSoftServe.go b/cmd/stepsSoftServe.go index 72dfc38d0..18b68766d 100644 --- a/cmd/stepsSoftServe.go +++ b/cmd/stepsSoftServe.go @@ -125,7 +125,7 @@ func configureSoftServe() { Auth: auth, }) if err != nil { - llog.Panicf("error pushing to remote", err) + log.Panicf("error pushing to remote", err) } } diff --git a/cmd/stepsVault.go b/cmd/stepsVault.go index b681ee448..ec0e05011 100644 --- a/cmd/stepsVault.go +++ b/cmd/stepsVault.go @@ -4,14 +4,14 @@ import ( "fmt" "log" "os" - "strings" "github.com/spf13/viper" "os/exec" - "encoding/json" - "bytes" - "encoding/base64" + "syscall" gitlab "github.com/xanzy/go-gitlab" vault "github.com/hashicorp/vault/api" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "github.com/kubefirst/nebulous/pkg/kubefirst" ) diff --git a/go.mod b/go.mod index 7904cb848..057f19c00 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,7 @@ go 1.17 require ( github.com/aws/aws-sdk-go v1.44.23 github.com/aws/aws-sdk-go-v2/config v1.15.7 - github.com/aws/aws-sdk-go-v2/service/ecr v1.17.6 github.com/aws/aws-sdk-go-v2/service/eks v1.21.1 - github.com/aws/aws-sdk-go-v2/service/kms v1.17.3 github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5 github.com/aws/aws-sdk-go-v2/service/sts v1.16.6 github.com/cip8/autoname v1.0.0 @@ -20,17 +18,15 @@ require ( github.com/spf13/viper v1.11.0 github.com/xanzy/go-gitlab v0.68.0 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 - k8s.io/api v0.24.2 - k8s.io/apimachinery v0.24.2 - k8s.io/client-go v0.24.2 - github.com/argoproj/argo-cd/v2 v2.0.5 + k8s.io/api v0.22.1 + k8s.io/apimachinery v0.22.1 + k8s.io/client-go v0.22.1 ) replace k8s.io/client-go => k8s.io/client-go v0.22.1 replace k8s.io/apimachinery => k8s.io/apimachinery v0.22.1 - require ( github.com/Microsoft/go-winio v0.5.1 // indirect github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect diff --git a/go.sum b/go.sum index 9bd545c66..f4c622914 100644 --- a/go.sum +++ b/go.sum @@ -111,14 +111,10 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 h1:eeXdGVtXEe+2Jc49+/v github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6/go.mod h1:FwpAKI+FBPIELJIdmQzlLtRe8LQSOreMcM2wBsPMvvc= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.12 h1:j0VqrjtgsY1Bx27tD0ysay36/K4kFMWRp9K3ieO9nLU= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.12/go.mod h1:00c7+ALdPh4YeEUPXJzyU0Yy01nPGOq2+9rUaz05z9g= -github.com/aws/aws-sdk-go-v2/service/ecr v1.17.6 h1:R9FxvsuknGAoKDJ1YRKwbgkTbedZZ++R7BwscG/6vRk= -github.com/aws/aws-sdk-go-v2/service/ecr v1.17.6/go.mod h1:+eCLloB5OdOr47npoEKlHGphSa72k44lXebO8I9LpKk= github.com/aws/aws-sdk-go-v2/service/eks v1.21.1 h1:CYRfUZNq2krWxqLE1ntvy40uVT/I6X1PniNHrE/W3ps= github.com/aws/aws-sdk-go-v2/service/eks v1.21.1/go.mod h1:kLodo8S0UEoVEd3mHTKtGnAhCT2uvCUM/Jjfr3og1yI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.5 h1:gRW1ZisKc93EWEORNJRvy/ZydF3o6xLSveJHdi1Oa0U= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.5/go.mod h1:ZbkttHXaVn3bBo/wpJbQGiiIWR90eTBUVBrEHUEQlho= -github.com/aws/aws-sdk-go-v2/service/kms v1.17.3 h1:M9bIvNNpbtvDTlZC5I38Kn2yuinJZ/9L+AM2Qom23zI= -github.com/aws/aws-sdk-go-v2/service/kms v1.17.3/go.mod h1:EKkrWWXwWYf8x3Nrm6Oix3zZP9NRBHqxw5buFGVBHA0= github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5 h1:Vex6D07/CmahT0LIaiRk+j9xyVAsvQxBa8iv38p3Ajc= github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5/go.mod h1:QZWV7sxHUg/qsPJcAtAI9JyLPKZ78weHmdILmYMCqEE= github.com/aws/aws-sdk-go-v2/service/sso v1.11.5 h1:TfJ/zuOYvHnxkvohSwAF3Ppn9KT/SrGZuOZHTPy8Guw= @@ -1124,9 +1120,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.22.1 h1:ISu3tD/jRhYfSW8jI/Q1e+lRxkR7w9UwQEZ7FgslrwY= k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= -k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI= -k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= k8s.io/apimachinery v0.22.1 h1:DTARnyzmdHMz7bFWFDDm22AM4pLWTQECMpRTFu2d2OM= k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/client-go v0.22.1 h1:jW0ZSHi8wW260FvcXHkIa0NLxFBQszTlhiAVsU5mopw= diff --git a/internal/argocd.go b/internal/argocd.go new file mode 100644 index 000000000..8a947588a --- /dev/null +++ b/internal/argocd.go @@ -0,0 +1,114 @@ +package internal + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "github.com/spf13/viper" + "io/ioutil" + "log" + "net/http" +) + +// kSyncArgocdApplication request ArgoCD to manual sync an application. Expected parameters are the ArgoCD application +// name and ArgoCD token with enough permission to perform the request against Argo API. When the http request returns +// status 200 it means a successful request/true, any other http status response return false. +func kSyncArgocdApplication(applicationName, argocdAuthToken string) (bool, error) { + + // todo: instantiate a new client on every http request is bad idea, we might need to set a new architecture to avoid + customTransport := http.DefaultTransport.(*http.Transport).Clone() + customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + httpClient := http.Client{Transport: customTransport} + + // todo: url values can be stored on .env files, and consumed when necessary to avoid hardcode urls + url := fmt.Sprintf("https://localhost:8081/api/v1/applications/%s/sync", applicationName) + req, err := http.NewRequest(http.MethodPost, url, nil) + if err != nil { + log.Println(err) + return false, err + } + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", argocdAuthToken)) + resp, err := httpClient.Do(req) + if err != nil { + log.Printf("error sending POST request to ArgoCD for syncing application (%s)\n", applicationName) + return false, err + } + + if resp.StatusCode == http.StatusOK { + return true, nil + } + return false, nil +} + +type ArgoCDConfig struct { + Username string `json:"username"` + Password string `json:"password"` +} + +// getArgoCDToken expects ArgoCD username and password, and returns a ArgoCD Bearer Token. +func getArgoCDToken(username string, password string) (string, error) { + + // todo: instantiate a new client on every http request is bad idea, we might need to set a new architecture to avoid + // todo: fast solution here is to have a singleton to avoid code duplication + customTransport := http.DefaultTransport.(*http.Transport).Clone() + customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + httpClient := http.Client{Transport: customTransport} + + // todo: move it to config + url := "https://localhost:8080/api/v1/session" + + // todo: this is documentation only, delete it when there is some function calling it + //Username: "admin", + //Password: viper.GetString("argocd.admin.password"), + + argoCDConfig := ArgoCDConfig{ + Username: username, + Password: password, + } + + payload, err := json.Marshal(argoCDConfig) + if err != nil { + return "", err + } + + req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(payload)) + if err != nil { + return "", err + } + + res, err := httpClient.Do(req) + if err != nil { + return "", err + } + + if res.StatusCode != http.StatusOK { + return "", errors.New("unable to retrieve ArgoCD token") + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", err + } + + var jsonReturn map[string]interface{} + err = json.Unmarshal(body, &jsonReturn) + if err != nil { + return "", err + } + token := fmt.Sprintf("%v", jsonReturn["token"]) + if len(token) == 0 { + return "", errors.New("unable to retrieve ArgoCD token, make sure ArgoCD credentials are correct") + } + + // update config file + viper.Set("argocd.admin.apitoken", token) + err = viper.WriteConfig() + if err != nil { + log.Println(err) + return "", err + } + + return token, nil +} diff --git a/pkg/kubefirst/kubefirst.go b/pkg/kubefirst/kubefirst.go new file mode 100644 index 000000000..d3a5c3022 --- /dev/null +++ b/pkg/kubefirst/kubefirst.go @@ -0,0 +1,48 @@ +package kubefirst + +import ( + "context" + "encoding/json" + "fmt" + "log" + + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" +) + +var vaultSecretClient coreV1Types.SecretInterface + +func GetPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { + + var gitlabToolboxPodName string + + pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) + if err != nil { + panic(fmt.Sprintf("error: failed to list using gitlab pods client %s", err)) + } + + gitlabToolboxPodName = pods.Items[0].Name + + return gitlabToolboxPodName +} + +func GetVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { + var vaultRootToken string + name := "vault-unseal-keys" + log.Printf("Reading secret %s\n", name) + secret, err := vaultSecretClient.Get(context.TODO(), name, metaV1.GetOptions{}) + + if err != nil { + panic(err.Error()) + } + + var jsonData map[string]interface{} + + for _, value := range secret.Data { + if err := json.Unmarshal(value, &jsonData); err != nil { + panic(err) + } + vaultRootToken = jsonData["root_token"].(string) + } + return vaultRootToken +} From a0508c3b99e68e8293e0d290ddad6be05b26e491 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:25:36 +0000 Subject: [PATCH 030/107] fix dry-run create Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/stepsArgo.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index 003f85d31..e6ca92b54 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -397,6 +397,10 @@ func changeRegistryToGitLab() { } func getArgocdAuthToken() string { + if dryrunMode { + log.Printf("[#99] Dry-run mode, getArgocdAuthToken skipped.") + return "nothing" + } kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr From 12e0d824f4a8d209da9fd2d75aad244481dde714 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 16:52:30 +0000 Subject: [PATCH 031/107] add some skip steps Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 68 +++++++++++---------- cmd/destroy.go | 128 +++++++++------------------------------- cmd/stepsArgo.go | 51 ++++++++++++++++ cmd/stepsBaseInstall.go | 26 ++++++++ 4 files changed, 143 insertions(+), 130 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 07c1f654a..131894f9b 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -18,6 +18,8 @@ const trackerStage20 = "0 - Apply Base" const trackerStage21 = "1 - Temporary SCM Install" const trackerStage22 = "2 - Argo/Final SCM Install" const trackerStage23 = "3 - Final Setup" +var skipVault bool +var skipGitlab bool // createCmd represents the create command var createCmd = &cobra.Command{ @@ -63,38 +65,40 @@ to quickly create a Cobra application.`, produceGitlabTokens() Trackers[trackerStage22].Tracker.Increment(int64(1)) + if !skipGitlab { + applyGitlabTerraform(directory) + Trackers[trackerStage22].Tracker.Increment(int64(1)) + gitlabKeyUpload() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + } - applyGitlabTerraform(directory) - Trackers[trackerStage22].Tracker.Increment(int64(1)) - gitlabKeyUpload() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - - configureVault() - Trackers[trackerStage23].Tracker.Increment(int64(1)) - - - addGitlabOidcApplications() - Trackers[trackerStage23].Tracker.Increment(int64(1)) - awaitGitlab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - - pushGitopsToGitLab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - changeRegistryToGitLab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - - - - hydrateGitlabMetaphorRepo() - Trackers[trackerStage23].Tracker.Increment(int64(1)) - - token := getArgocdAuthToken() - syncArgocdApplication("argo-components", token) - syncArgocdApplication("gitlab-runner-components", token) - syncArgocdApplication("gitlab-runner", token) - syncArgocdApplication("atlantis-components", token) - syncArgocdApplication("chartmuseum-components", token) - + if !skipVault || !skipGitlab { + configureVault() + Trackers[trackerStage23].Tracker.Increment(int64(1)) + + + addGitlabOidcApplications() + Trackers[trackerStage23].Tracker.Increment(int64(1)) + awaitGitlab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + + pushGitopsToGitLab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + changeRegistryToGitLab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + + + + hydrateGitlabMetaphorRepo() + Trackers[trackerStage23].Tracker.Increment(int64(1)) + + token := getArgocdAuthToken() + syncArgocdApplication("argo-components", token) + syncArgocdApplication("gitlab-runner-components", token) + syncArgocdApplication("gitlab-runner", token) + syncArgocdApplication("atlantis-components", token) + syncArgocdApplication("chartmuseum-components", token) + } metricName = "kubefirst.mgmt_cluster_install.completed" @@ -123,5 +127,7 @@ func init() { // todo make this an optional switch and check for it or viper createCmd.Flags().Bool("destroy", false, "destroy resources") createCmd.PersistentFlags().BoolVarP(&dryrunMode, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") + createCmd.PersistentFlags().BoolVar(&skipVault, "skip-vault", false, "Skip post-git lab install and vault setup") + createCmd.PersistentFlags().BoolVar(&skipGitlab, "skip-gitlab", false, "Skip git lab install and vault setup") } diff --git a/cmd/destroy.go b/cmd/destroy.go index 2d34f6546..11b8fc491 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -10,12 +10,13 @@ import ( "os" "os/exec" "syscall" - "time" - "github.com/spf13/cobra" "github.com/spf13/viper" ) +var skipGitlabTerraform bool +var skipDeleteRegistryApplication bool +var skipBaseTerraform bool // destroyCmd represents the destroy command var destroyCmd = &cobra.Command{ Use: "destroy", @@ -36,115 +37,44 @@ if the registry has already been delteted.`, log.Panicf("error: failed to port-forward to gitlab %s", err) } // todo this needs to be removed when we are no longer in the starter account - - log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") - // kubeconfig := os.Getenv("HOME") + "/.kube/config" - // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) - // argocdclientset, err := argocdclientset.NewForConfig(config) - // if err != nil { - // return nil, err - // } - - //* should we git clone the gitops repo when destroy is run back to their - //* local host to get the latest values of gitops - - os.Setenv("AWS_REGION", viper.GetString("aws.region")) - os.Setenv("AWS_ACCOUNT_ID", viper.GetString("aws.accountid")) - os.Setenv("HOSTED_ZONE_NAME", viper.GetString("aws.hostedzonename")) - os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) - - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) - skipGitlabTerraform, _ := cmd.Flags().GetBool("skip-gitlab-terraform") - - err = os.Chdir(directory) - if err != nil { - log.Panicf("error: could not change directory to " + directory) - } - - os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") - - if !skipGitlabTerraform { - tfInitGitlabCmd := exec.Command(terraformPath, "init") - tfInitGitlabCmd.Stdout = os.Stdout - tfInitGitlabCmd.Stderr = os.Stderr - err = tfInitGitlabCmd.Run() - if err != nil { - log.Panicf("failed to terraform init gitlab %s", err) - } - - tfDestroyGitlabCmd := exec.Command(terraformPath, "destroy", "-auto-approve") - tfDestroyGitlabCmd.Stdout = os.Stdout - tfDestroyGitlabCmd.Stderr = os.Stderr - err = tfDestroyGitlabCmd.Run() - if err != nil { - log.Panicf("failed to terraform destroy gitlab %s", err) - } - - viper.Set("destroy.terraformdestroy.gitlab", true) - viper.WriteConfig() - } - - directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) - err = os.Chdir(directory) - if err != nil { - log.Panicf("error: could not change directory to " + directory) - } - + destroyGitlabTerraform() // delete argocd registry deleteRegistryApplication() - log.Println("sleeping for 42 seconds to allow the registry to delete") - time.Sleep(42 * time.Second) - - tfInitBaseCmd := exec.Command(terraformPath, "init") - tfInitBaseCmd.Stdout = os.Stdout - tfInitBaseCmd.Stderr = os.Stderr - err = tfInitBaseCmd.Run() - if err != nil { - log.Panicf("failed to terraform init base %s", err) - } + destroyBaseTerraform() + //TODO: Remove buckets? Opt-in flag - tfDestroyBaseCmd := exec.Command(terraformPath, "destroy", "-auto-approve") - tfDestroyBaseCmd.Stdout = os.Stdout - tfDestroyBaseCmd.Stderr = os.Stderr - err = tfDestroyBaseCmd.Run() - if err != nil { - log.Panicf("failed to terraform destroy base %s", err) - } - - viper.Set("destroy.terraformdestroy.base", true) - viper.WriteConfig() }, } func init() { - nebulousCmd.AddCommand(destroyCmd) + rootCmd.AddCommand(destroyCmd) - destroyCmd.Flags().Bool("skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") + destroyCmd.PersistentFlags().BoolVar(&skipGitlabTerraform, "skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") + destroyCmd.PersistentFlags().BoolVar(&skipDeleteRegistryApplication, "skip-delete-register", false, "whether to skip deletion of resgister application ") + destroyCmd.PersistentFlags().BoolVar(&skipBaseTerraform, "skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") } func deleteRegistryApplication() { - log.Println("starting port forward to argocd server and deleting registry") - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to argocd %s", err) - } + if !skipDeleteRegistryApplication { + log.Println("starting port forward to argocd server and deleting registry") + kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd %s", err) + } - url := "https://localhost:8080/api/v1/applications/registry" - argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) - argoCdAppSync.Stdout = os.Stdout - argoCdAppSync.Stderr = os.Stderr - err = argoCdAppSync.Run() - if err != nil { - log.Panicf("error: curl appSync failed failed %s", err) + url := "https://localhost:8080/api/v1/applications/registry" + argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) + argoCdAppSync.Stdout = os.Stdout + argoCdAppSync.Stderr = os.Stderr + err = argoCdAppSync.Run() + if err != nil { + log.Panicf("error: curl appSync failed failed %s", err) + } + log.Println("deleting argocd application registry") } - log.Println("deleting argocd application registry") } \ No newline at end of file diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index e6ca92b54..fcd6d64c1 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -476,4 +476,55 @@ func syncArgocdApplication(applicationName, argocdAuthToken string) { if err != nil { log.Panicf("error: curl appSync failed failed %s", err) } +} + +func destroyGitlabTerraform(){ + log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") + // kubeconfig := os.Getenv("HOME") + "/.kube/config" + // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) + // argocdclientset, err := argocdclientset.NewForConfig(config) + // if err != nil { + // return nil, err + // } + + //* should we git clone the gitops repo when destroy is run back to their + //* local host to get the latest values of gitops + + os.Setenv("AWS_REGION", viper.GetString("aws.region")) + os.Setenv("AWS_ACCOUNT_ID", viper.GetString("aws.accountid")) + os.Setenv("HOSTED_ZONE_NAME", viper.GetString("aws.hostedzonename")) + os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) + + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) + + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) + err := os.Chdir(directory) + if err != nil { + log.Panicf("error: could not change directory to " + directory) + } + + os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") + + if !skipGitlabTerraform { + tfInitGitlabCmd := exec.Command(terraformPath, "init") + tfInitGitlabCmd.Stdout = os.Stdout + tfInitGitlabCmd.Stderr = os.Stderr + err = tfInitGitlabCmd.Run() + if err != nil { + log.Panicf("failed to terraform init gitlab %s", err) + } + + tfDestroyGitlabCmd := exec.Command(terraformPath, "destroy", "-auto-approve") + tfDestroyGitlabCmd.Stdout = os.Stdout + tfDestroyGitlabCmd.Stderr = os.Stderr + err = tfDestroyGitlabCmd.Run() + if err != nil { + log.Panicf("failed to terraform destroy gitlab %s", err) + } + + viper.Set("destroy.terraformdestroy.gitlab", true) + viper.WriteConfig() + } } \ No newline at end of file diff --git a/cmd/stepsBaseInstall.go b/cmd/stepsBaseInstall.go index d078cf844..5610e6ae2 100644 --- a/cmd/stepsBaseInstall.go +++ b/cmd/stepsBaseInstall.go @@ -48,4 +48,30 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ } else { log.Println("Skipping: ApplyBaseTerraform") } +} + +func destroyBaseTerraform(){ + if !skipBaseTerraform { + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) + err := os.Chdir(directory) + if err != nil { + log.Panicf("error: could not change directory to " + directory) + } + + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) + + _, _, errInit := execShellReturnStrings(terraformPath, "init") + if errInit != nil { + log.Panicf("failed to terraform init base %s", err) + } + + _, _, errDestroy := execShellReturnStrings(terraformPath, "destroy", "-auto-approve") + if errDestroy != nil { + log.Panicf("failed to terraform destroy base %s", err) + } + viper.Set("destroy.terraformdestroy.base", true) + viper.WriteConfig() + } } \ No newline at end of file From 145af0f5315ed3093518bd6ebee444e31ab7dd2c Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 5 Jul 2022 18:33:00 +0000 Subject: [PATCH 032/107] Adding skip steps Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 62 ++++++++++++++++++++--------------------- cmd/shell.go | 3 ++ cmd/stepsBaseInstall.go | 12 ++++---- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 131894f9b..adbded4ba 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -62,42 +62,42 @@ to quickly create a Cobra application.`, helmInstallArgocd(home, kubeconfigPath) Trackers[trackerStage22].Tracker.Increment(int64(1)) - produceGitlabTokens() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - - if !skipGitlab { + if !skipGitlab { + //TODO: Confirm if we need to waitgit lab to be ready + // OR something, too fast the secret will not be there. + awaitGitlab() + produceGitlabTokens() + Trackers[trackerStage22].Tracker.Increment(int64(1)) applyGitlabTerraform(directory) Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlabKeyUpload() Trackers[trackerStage22].Tracker.Increment(int64(1)) - } - if !skipVault || !skipGitlab { - configureVault() - Trackers[trackerStage23].Tracker.Increment(int64(1)) - - - addGitlabOidcApplications() - Trackers[trackerStage23].Tracker.Increment(int64(1)) - awaitGitlab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - - pushGitopsToGitLab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - changeRegistryToGitLab() - Trackers[trackerStage22].Tracker.Increment(int64(1)) - - - - hydrateGitlabMetaphorRepo() - Trackers[trackerStage23].Tracker.Increment(int64(1)) - - token := getArgocdAuthToken() - syncArgocdApplication("argo-components", token) - syncArgocdApplication("gitlab-runner-components", token) - syncArgocdApplication("gitlab-runner", token) - syncArgocdApplication("atlantis-components", token) - syncArgocdApplication("chartmuseum-components", token) + if !skipVault { + configureVault() + Trackers[trackerStage23].Tracker.Increment(int64(1)) + addGitlabOidcApplications() + Trackers[trackerStage23].Tracker.Increment(int64(1)) + awaitGitlab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + + pushGitopsToGitLab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + changeRegistryToGitLab() + Trackers[trackerStage22].Tracker.Increment(int64(1)) + + + + hydrateGitlabMetaphorRepo() + Trackers[trackerStage23].Tracker.Increment(int64(1)) + + token := getArgocdAuthToken() + syncArgocdApplication("argo-components", token) + syncArgocdApplication("gitlab-runner-components", token) + syncArgocdApplication("gitlab-runner", token) + syncArgocdApplication("atlantis-components", token) + syncArgocdApplication("chartmuseum-components", token) + } } metricName = "kubefirst.mgmt_cluster_install.completed" diff --git a/cmd/shell.go b/cmd/shell.go index f1d1316d2..54b29c8c7 100644 --- a/cmd/shell.go +++ b/cmd/shell.go @@ -15,5 +15,8 @@ func execShellReturnStrings(command string, args ...string) (string, string, err if err != nil { log.Println("Error executing command: %v", err) } + log.Println("Commad Execution: %s", command) + log.Println("Commad Execution STDOUT: %s", outb.String()) + log.Println("Commad Execution STDERR: %s", errb.String()) return outb.String(), errb.String(), err } diff --git a/cmd/stepsBaseInstall.go b/cmd/stepsBaseInstall.go index 5610e6ae2..54c67ebde 100644 --- a/cmd/stepsBaseInstall.go +++ b/cmd/stepsBaseInstall.go @@ -23,19 +23,19 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ err := os.Chdir(directory) if err != nil { - log.Panicf("error, directory does not exist - did you `kubefirst init`?: %s \nerror: %s", directory, err) + log.Panicf("error, directory does not exist - did you `kubefirst init`?: %s \nerror: %v", directory, err) } _,_,errInit := execShellReturnStrings(terraformPath, "init") if errInit != nil { - panic(fmt.Sprintf("error: terraform init failed %s", err)) + panic(fmt.Sprintf("error: terraform init failed %v", err)) } _,_,errApply := execShellReturnStrings(terraformPath,"apply", "-auto-approve") if errApply != nil { - panic(fmt.Sprintf("error: terraform init failed %s", err)) + panic(fmt.Sprintf("error: terraform init failed %v", err)) } keyOut, _, errKey := execShellReturnStrings(terraformPath, "output", "vault_unseal_kms_key") if errKey != nil { - log.Panicf("error: terraform apply failed %s", err) + log.Panicf("error: terraform apply failed %v", err) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) keyIdNoSpace := strings.TrimSpace(keyOut) @@ -64,12 +64,12 @@ func destroyBaseTerraform(){ _, _, errInit := execShellReturnStrings(terraformPath, "init") if errInit != nil { - log.Panicf("failed to terraform init base %s", err) + log.Panicf("failed to terraform init base %v", err) } _, _, errDestroy := execShellReturnStrings(terraformPath, "destroy", "-auto-approve") if errDestroy != nil { - log.Panicf("failed to terraform destroy base %s", err) + log.Panicf("failed to terraform destroy base %v", err) } viper.Set("destroy.terraformdestroy.base", true) viper.WriteConfig() From 65bfc9d94ab340f55fe786195747b554f6551b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 6 Jul 2022 09:11:01 -0300 Subject: [PATCH 033/107] feat: add docker compose to kubefirst-dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- README.md | 72 +++++++++++++++++++++++----------------- build/Dockerfile | 34 +++++++++++++++++++ docker-compose.yaml | 14 ++++++++ scripts/kubefirst-dev.sh | 7 ++++ 4 files changed, 97 insertions(+), 30 deletions(-) create mode 100644 build/Dockerfile create mode 100644 docker-compose.yaml create mode 100755 scripts/kubefirst-dev.sh diff --git a/README.md b/README.md index e002d957d..87c71ee84 100644 --- a/README.md +++ b/README.md @@ -129,42 +129,57 @@ docker run -it --env-file=kubefirst.env -v $PWD/gitops:/gitops -v $PWD/metaphor: ### New README -# Flare +# Kubefirst CLI -- [Flare](#flare) - - [Start](#start) - - [Start Environment variables](#start-environment-variables) - - [Start Actions](#start-actions) - - [Start Confirmation](#start-confirmation) - - [Destroy](#destroy) - - [Destroy Actions](#destroy-actions) - - [Notes:](#notes) +Kubefirst CLI is a cloud provisioning tool. With simple setup and few CLI calls, we spin up a full AWS cluster with full +GitOps integration, secrets management, production and development Kubernetes environments ready to be consumed. -## Start +- [Setup](#setup) +- [Start the container](#start-the-container) +- [Initialization](#initialization) +- [Creation](#creation) +- [Access ArgoCD](#access-argocd) +- [Destroy](#destroy) -### Start Environment variables +## Setup -In order to start Kubefirst, the required environment variables are: +The setup is extremely simple, create a `.env` file in the root folder, and add the following variables: -| Variable | example | -|------------------|--------------------| -| AWS_PROFILE | default | -| AWS_REGION | us-east-1 | -| HOSTED_ZONE_NAME | mydomain.com | -| ADMIN_EMAIL | myemail@somewhere.com | +| Variable | example | +|--------------------|------------------| +| AWS_PROFILE | default | +| CLOUD_PROVIDER=aws | aws | +| AWS_REGION | us-east-1 | +| HOSTED_ZONE_NAME | example.com | +| ADMIN_EMAIL | john@example.com | -### Start Actions +## Start the container + +We run everything on isolation with Docker, for that, start the container with: + +```bash +docker-compose up kubefirst-dev +``` + +## Initialization + +Some process requires previous initialization, for that, run: ```bash touch ~/.flare mkdir -p ~/.kubefirst -cd ~/git/kubefirst/gitlab/flare # change to your dir if different -go build -o bin/flare main.go -./bin/flare nebulous init --admin-email $ADMIN_EMAIL --cloud aws --hosted-zone-name $HOSTED_ZONE_NAME --region $AWS_REGION +go run . nebulous init --admin-email $ADMIN_EMAIL --cloud $CLOUD_PROVIDER --hosted-zone-name $HOSTED_ZONE_NAME --region $AWS_REGION +``` + +## Creation + +At this point, everything is ready to start provisioning the cloud services, and for that we can run: + +```bash ./bin/flare nebulous create ``` -### Start Confirmation +## Access ArgoCD ```bash aws eks update-kubeconfig --name kubefirst @@ -176,18 +191,16 @@ kubectl -n argocd port-forward svc/argocd-server 8080:80 To destroy remote then local. -These environment variables are expected: +These environment variables are expected: (todo: move all env. variable references to the setup section) | Variable | example | |------------------|-----------------------------------------------------------------------------------------------| | AWS_PROFILE | default | | AWS_REGION | us-east-1 | -| AWS_ACCOUNT_ID | 1xxxxxxxxxx4 | -| HOSTED_ZONE_NAME | mydomain.com | +| AWS_ACCOUNT_ID | 000000000 | +| HOSTED_ZONE_NAME | kubefast.com | | GITLAB_TOKEN | "xxxxx1-xx1x-x1xx-1" # replace with value from ~/.flare (only needed if you got to gitlab tf) | - -### Destroy Actions ```bash ./bin/flare nebulous destroy rm -rf ~/.kubefirst @@ -196,6 +209,5 @@ rm ~/.flare #### Notes: -added gitlab.yaml to registry +added gitlab.yaml to registry pushing local to soft origin - diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 000000000..c3aea0dcf --- /dev/null +++ b/build/Dockerfile @@ -0,0 +1,34 @@ +FROM --platform=linux/amd64 golang:1.18 + +WORKDIR /opt/kubefirst + +RUN apt-get update && \ + apt-get install -y unzip + + +# enable terminal vi mode +RUN set -o vi + +# Kubernetes client +RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.21.3/bin/linux/amd64/kubectl && \ + chmod +x ./kubectl && \ + mv kubectl /usr/local/bin/ + +# AWS cli +RUN curl -LO https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip && \ + unzip awscli-exe-linux-x86_64.zip && \ + ./aws/install + +# AWS EKS cli +RUN curl -LO https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_linux_amd64.tar.gz && \ + tar -xvzf eksctl_linux_amd64.tar.gz -C /usr/local/bin/ + +# AWS IAM Authenticator tool +RUN curl -LO https://s3.us-west-2.amazonaws.com/amazon-eks/1.21.2/2021-07-05/bin/linux/amd64/aws-iam-authenticator && \ + chmod +x aws-iam-authenticator && \ + mv aws-iam-authenticator /usr/local/bin/ + +# setup user +RUN useradd -ms /bin/bash developer +USER developer +WORKDIR /home/developer/kubefirst diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 000000000..2f193a56c --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,14 @@ +version: "3" + +services: + + kubefirst-dev: + platform: linux/amd64 + build: + context: ./build + container_name: kubefirst-dev + volumes: + - ./:/home/developer/kubefirst + env_file: + - .env + command: sh -c "./scripts/kubefirst-dev.sh" diff --git a/scripts/kubefirst-dev.sh b/scripts/kubefirst-dev.sh new file mode 100755 index 000000000..c81a2b218 --- /dev/null +++ b/scripts/kubefirst-dev.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +echo "----------------------------------------------------------------------" +echo "- kubefirst-dev container is ready! -" +echo "- Attach to docker container: \"docker exec -it kubefirst-dev bash\" -" +echo "----------------------------------------------------------------------" +sleep infinity From a98e42120774d31247d6b0b76d64b0a2428084d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 6 Jul 2022 11:02:47 -0300 Subject: [PATCH 034/107] refactor: update gitlab folder structure, and clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 59 +++++++-------------- internal/{ => argocd}/argocd.go | 2 +- pkg/ssh/ssh.go => internal/gitlab/gitlab.go | 20 +++---- 3 files changed, 30 insertions(+), 51 deletions(-) rename internal/{ => argocd}/argocd.go (99%) rename pkg/ssh/ssh.go => internal/gitlab/gitlab.go (52%) diff --git a/cmd/create.go b/cmd/create.go index 07c1f654a..eeaa94234 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -1,19 +1,14 @@ -/* -Copyright © 2022 NAME HERE - -*/ package cmd import ( "fmt" - "log" - "time" + "github.com/kubefirst/nebulous/pkg/flare" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/kubefirst/nebulous/pkg/flare" + "log" + "time" ) - const trackerStage20 = "0 - Apply Base" const trackerStage21 = "1 - Temporary SCM Install" const trackerStage22 = "2 - Argo/Final SCM Install" @@ -33,10 +28,10 @@ to quickly create a Cobra application.`, flare.SetupProgress(4) Trackers = make(map[string]*flare.ActionTracker) - Trackers[trackerStage20] = &flare.ActionTracker{flare.CreateTracker(trackerStage20, int64(1))} - Trackers[trackerStage21] = &flare.ActionTracker{flare.CreateTracker(trackerStage21, int64(2))} - Trackers[trackerStage22] = &flare.ActionTracker{flare.CreateTracker(trackerStage22, int64(7))} - Trackers[trackerStage23] = &flare.ActionTracker{flare.CreateTracker(trackerStage23, int64(3))} + Trackers[trackerStage20] = &flare.ActionTracker{Tracker: flare.CreateTracker(trackerStage20, int64(1))} + Trackers[trackerStage21] = &flare.ActionTracker{Tracker: flare.CreateTracker(trackerStage21, int64(2))} + Trackers[trackerStage22] = &flare.ActionTracker{Tracker: flare.CreateTracker(trackerStage22, int64(7))} + Trackers[trackerStage23] = &flare.ActionTracker{Tracker: flare.CreateTracker(trackerStage23, int64(3))} infoCmd.Run(cmd, args) @@ -48,46 +43,41 @@ to quickly create a Cobra application.`, } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) - applyBaseTerraform(cmd,directory) - Trackers[trackerStage20].Tracker.Increment(int64(1)) + applyBaseTerraform(cmd, directory) + Trackers[trackerStage20].Tracker.Increment(int64(1)) createSoftServe(kubeconfigPath) - Trackers[trackerStage21].Tracker.Increment(int64(1)) - configureSoftserveAndPush() - Trackers[trackerStage21].Tracker.Increment(int64(1)) + Trackers[trackerStage21].Tracker.Increment(int64(1)) + configureSoftserveAndPush() + Trackers[trackerStage21].Tracker.Increment(int64(1)) helmInstallArgocd(home, kubeconfigPath) Trackers[trackerStage22].Tracker.Increment(int64(1)) produceGitlabTokens() Trackers[trackerStage22].Tracker.Increment(int64(1)) - - + applyGitlabTerraform(directory) Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlabKeyUpload() Trackers[trackerStage22].Tracker.Increment(int64(1)) - + configureVault() Trackers[trackerStage23].Tracker.Increment(int64(1)) - addGitlabOidcApplications() Trackers[trackerStage23].Tracker.Increment(int64(1)) awaitGitlab() Trackers[trackerStage22].Tracker.Increment(int64(1)) - + pushGitopsToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) changeRegistryToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) - - - + hydrateGitlabMetaphorRepo() Trackers[trackerStage23].Tracker.Increment(int64(1)) - + token := getArgocdAuthToken() syncArgocdApplication("argo-components", token) syncArgocdApplication("gitlab-runner-components", token) @@ -95,9 +85,8 @@ to quickly create a Cobra application.`, syncArgocdApplication("atlantis-components", token) syncArgocdApplication("chartmuseum-components", token) - metricName = "kubefirst.mgmt_cluster_install.completed" - + if !dryrunMode { flare.SendTelemetry(metricDomain, metricName) } else { @@ -107,20 +96,10 @@ to quickly create a Cobra application.`, }, } - - - - - - - - func init() { rootCmd.AddCommand(createCmd) - // createCmd.Flags().String("tf-entrypoint", "", "the entrypoint to execute the terraform from") - // createCmd.MarkFlagRequired("tf-entrypoint") - // todo make this an optional switch and check for it or viper + // todo: make this an optional switch and check for it or viper createCmd.Flags().Bool("destroy", false, "destroy resources") createCmd.PersistentFlags().BoolVarP(&dryrunMode, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") diff --git a/internal/argocd.go b/internal/argocd/argocd.go similarity index 99% rename from internal/argocd.go rename to internal/argocd/argocd.go index 8a947588a..9cadc6e15 100644 --- a/internal/argocd.go +++ b/internal/argocd/argocd.go @@ -1,4 +1,4 @@ -package internal +package argocd import ( "bytes" diff --git a/pkg/ssh/ssh.go b/internal/gitlab/gitlab.go similarity index 52% rename from pkg/ssh/ssh.go rename to internal/gitlab/gitlab.go index 20a360eff..c1a007a04 100644 --- a/pkg/ssh/ssh.go +++ b/internal/gitlab/gitlab.go @@ -1,4 +1,4 @@ -package ssh +package gitlab import ( "crypto/rand" @@ -9,12 +9,7 @@ import ( "golang.org/x/crypto/ssh" ) -func marshalRSAPrivate(priv *rsa.PrivateKey) string { - return string(pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv), - })) -} - +// GenerateKey generate public and private keys to be consumed by GitLab. func GenerateKey() (string, string, error) { reader := rand.Reader bitSize := 2048 @@ -28,8 +23,13 @@ func GenerateKey() (string, string, error) { if err != nil { return "", "", err } - pubKeyStr := string(ssh.MarshalAuthorizedKey(pub)) - privKeyStr := marshalRSAPrivate(key) + publicKey := string(ssh.MarshalAuthorizedKey(pub)) + // encode RSA key + privateKey := string(pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), + }, + )) - return pubKeyStr, privKeyStr, nil + return publicKey, privateKey, nil } From 0d8d641ee81e332cf5614df79240d145b3db0a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 6 Jul 2022 14:40:53 -0300 Subject: [PATCH 035/107] refactor: update vault calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/init.go | 16 ++++----- cmd/stepsVault.go | 22 ++++++------ .../kubefirst.go => internal/vault/vault.go | 35 +++++-------------- 3 files changed, 26 insertions(+), 47 deletions(-) rename pkg/kubefirst/kubefirst.go => internal/vault/vault.go (51%) diff --git a/cmd/init.go b/cmd/init.go index 434d43caf..6093357d2 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -6,14 +6,15 @@ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/internal/gitlab" + "github.com/kubefirst/nebulous/pkg/flare" + "github.com/spf13/cobra" + "github.com/spf13/viper" "io/ioutil" "log" "strings" "time" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/kubefirst/nebulous/pkg/flare" - gitlabSsh "github.com/kubefirst/nebulous/pkg/ssh" + //gitlabSsh "github.com/kubefirst/nebulous/pkg/ssh" ) var Trackers map[string]*flare.ActionTracker @@ -63,7 +64,6 @@ to quickly create a Cobra application.`, log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } - // todo need to check flags and create config // hosted zone name: @@ -168,13 +168,11 @@ func init() { } - - func createSshKeyPair() { publicKey := viper.GetString("botpublickey") if publicKey == "" { log.Println("generating new key pair") - publicKey, privateKey, _ := gitlabSsh.GenerateKey() + publicKey, privateKey, _ := gitlab.GenerateKey() viper.Set("botPublicKey", publicKey) viper.Set("botPrivateKey", privateKey) err := viper.WriteConfig() @@ -227,5 +225,3 @@ configs: log.Panicf("error: could not write argocd-init-values.yaml %s", err) } } - - diff --git a/cmd/stepsVault.go b/cmd/stepsVault.go index ec0e05011..a138a9b48 100644 --- a/cmd/stepsVault.go +++ b/cmd/stepsVault.go @@ -2,19 +2,18 @@ package cmd import ( "fmt" - "log" - "os" + vault "github.com/hashicorp/vault/api" + internalVault "github.com/kubefirst/nebulous/internal/vault" "github.com/spf13/viper" - "os/exec" - "syscall" gitlab "github.com/xanzy/go-gitlab" - vault "github.com/hashicorp/vault/api" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" - "github.com/kubefirst/nebulous/pkg/kubefirst" + "log" + "os" + "os/exec" + "syscall" ) - func configureVault() { if !viper.GetBool("create.terraformapplied.vault") { if dryrunMode { @@ -44,7 +43,10 @@ func configureVault() { } vaultSecretClient = clientset.CoreV1().Secrets("vault") - vaultToken := kubefirst.GetVaultRootToken(vaultSecretClient) + vaultToken, err := internalVault.GetVaultRootToken(vaultSecretClient) + if err != nil { + log.Panicf("unable to get vault root token, error: %s", err) + } viper.Set("vault.token", vaultToken) viper.WriteConfig() @@ -103,7 +105,7 @@ func configureVault() { } func addGitlabOidcApplications() { - //TODO: Should this skipped if already executed. + //TODO: Should this skipped if already executed. if dryrunMode { log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") return @@ -189,4 +191,4 @@ func addVaultSecret(secretPath string, secretData map[string]interface{}) { } else { log.Println("secret successfully written to path: ", secretPath) } -} \ No newline at end of file +} diff --git a/pkg/kubefirst/kubefirst.go b/internal/vault/vault.go similarity index 51% rename from pkg/kubefirst/kubefirst.go rename to internal/vault/vault.go index d3a5c3022..75ba6d0c2 100644 --- a/pkg/kubefirst/kubefirst.go +++ b/internal/vault/vault.go @@ -1,48 +1,29 @@ -package kubefirst +package vault import ( "context" "encoding/json" - "fmt" - "log" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" + "log" ) -var vaultSecretClient coreV1Types.SecretInterface - -func GetPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { - - var gitlabToolboxPodName string - - pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) - if err != nil { - panic(fmt.Sprintf("error: failed to list using gitlab pods client %s", err)) - } - - gitlabToolboxPodName = pods.Items[0].Name - - return gitlabToolboxPodName -} - -func GetVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { - var vaultRootToken string +// GetVaultRootToken get `vault-unseal-keys` token on Vault. +func GetVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) (string, error) { name := "vault-unseal-keys" log.Printf("Reading secret %s\n", name) secret, err := vaultSecretClient.Get(context.TODO(), name, metaV1.GetOptions{}) - if err != nil { - panic(err.Error()) + return "", err } + var vaultRootToken string var jsonData map[string]interface{} - for _, value := range secret.Data { if err := json.Unmarshal(value, &jsonData); err != nil { - panic(err) + return "", err } vaultRootToken = jsonData["root_token"].(string) } - return vaultRootToken + return vaultRootToken, nil } From 18843fe1ddf64746235ac105cf1cd687b6336b16 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 6 Jul 2022 17:47:08 +0000 Subject: [PATCH 036/107] Add destroy buckets Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/aws.go | 75 +++++++++++++++++++++++++++++++++++++++++ cmd/destroy.go | 5 +++ cmd/stepsArgo.go | 2 ++ cmd/stepsBaseInstall.go | 2 ++ 4 files changed, 84 insertions(+) diff --git a/cmd/aws.go b/cmd/aws.go index cb8de00d6..5ed216924 100644 --- a/cmd/aws.go +++ b/cmd/aws.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/cip8/autoname" "github.com/spf13/viper" "log" @@ -246,4 +247,78 @@ func getDNSInfo(hostedZoneName string) string { func returnHostedZoneId(rawZoneId string) string { return strings.Split(rawZoneId, "/")[2] +} + + +func listBucketsInUse() []string{ + //Read flare file + //Iterate over buckets + //check if bucket exist + //buckets := make([]map[string]string, 0) + //var m map[string]string + var bucketsInUse []string + bucketsConfig := viper.AllKeys() + for _, bucketKey := range bucketsConfig { + match := strings.HasPrefix(bucketKey,"bucket.") && strings.HasSuffix(bucketKey,".name") + if match { + bucketName := viper.GetString(bucketKey) + bucketsInUse = append(bucketsInUse,bucketName) + } + } + return bucketsInUse +} + +func destroyBucket(bucketName string) { + + s3Client := s3.New(getAWSSession()) + + log.Printf("Attempt to delete: %s", bucketName) + _, errHead := s3Client.HeadBucket(&s3.HeadBucketInput{ + Bucket: &bucketName, + }) + if errHead != nil { + if aerr, ok := errHead.(awserr.Error); ok { + switch aerr.Code() { + case s3.ErrCodeNoSuchBucket: + log.Println("Bucket Error:", s3.ErrCodeNoSuchBucket, aerr.Error()) + default: + log.Println("Bucket Error:",aerr.Error()) + } + } else { + // Print the error, cast err to awserr.Error to get the Code and + // Message from an error. + log.Println(errHead.Error()) + } + } else { + //if exist, we can delete it + _, err := s3Client.DeleteBucket(&s3.DeleteBucketInput{ + Bucket: &bucketName, + }) + if err != nil { + log.Panicf("failed to delete bucket "+bucketName, err.Error()) + } + + } + +} + +func getAWSSession() *session.Session { + sess, err := session.NewSession(&aws.Config{ + Region: aws.String(viper.GetString("aws.region"))}, + ) + if err != nil { + log.Panicf("failed to get session ", err.Error()) + } + return sess +} + +func destroyBucketsInUse(){ + if destroyBuckets { + log.Println("Execute: destroyBucketsInUse") + for _,bucket := range listBucketsInUse() { + destroyBucket(bucket) + } + } else { + log.Println("Skip: destroyBucketsInUse") + } } \ No newline at end of file diff --git a/cmd/destroy.go b/cmd/destroy.go index 11b8fc491..12373b416 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -17,6 +17,7 @@ import ( var skipGitlabTerraform bool var skipDeleteRegistryApplication bool var skipBaseTerraform bool +var destroyBuckets bool // destroyCmd represents the destroy command var destroyCmd = &cobra.Command{ Use: "destroy", @@ -42,6 +43,7 @@ if the registry has already been delteted.`, deleteRegistryApplication() destroyBaseTerraform() //TODO: Remove buckets? Opt-in flag + destroyBucketsInUse() }, } @@ -52,6 +54,7 @@ func init() { destroyCmd.PersistentFlags().BoolVar(&skipGitlabTerraform, "skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") destroyCmd.PersistentFlags().BoolVar(&skipDeleteRegistryApplication, "skip-delete-register", false, "whether to skip deletion of resgister application ") destroyCmd.PersistentFlags().BoolVar(&skipBaseTerraform, "skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") + destroyCmd.PersistentFlags().BoolVar(&destroyBuckets, "destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") } @@ -76,5 +79,7 @@ func deleteRegistryApplication() { log.Panicf("error: curl appSync failed failed %s", err) } log.Println("deleting argocd application registry") + } else { + log.Println("skip: deleteRegistryApplication") } } \ No newline at end of file diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index fcd6d64c1..60abcfb9f 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -526,5 +526,7 @@ func destroyGitlabTerraform(){ viper.Set("destroy.terraformdestroy.gitlab", true) viper.WriteConfig() + } else { + log.Println("skip: destroyGitlabTerraform") } } \ No newline at end of file diff --git a/cmd/stepsBaseInstall.go b/cmd/stepsBaseInstall.go index 54c67ebde..1ac41f01a 100644 --- a/cmd/stepsBaseInstall.go +++ b/cmd/stepsBaseInstall.go @@ -73,5 +73,7 @@ func destroyBaseTerraform(){ } viper.Set("destroy.terraformdestroy.base", true) viper.WriteConfig() + } else { + log.Println("skip: destroyBaseTerraform") } } \ No newline at end of file From ca6a4a3172ab89adb2ec95b95f839fe78d624f51 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 6 Jul 2022 18:00:11 +0000 Subject: [PATCH 037/107] added new lines Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/destroy.go | 2 +- cmd/stepsArgo.go | 2 +- cmd/stepsBaseInstall.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/destroy.go b/cmd/destroy.go index 12373b416..8d643711a 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -82,4 +82,4 @@ func deleteRegistryApplication() { } else { log.Println("skip: deleteRegistryApplication") } -} \ No newline at end of file +} diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index 60abcfb9f..342d54222 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -529,4 +529,4 @@ func destroyGitlabTerraform(){ } else { log.Println("skip: destroyGitlabTerraform") } -} \ No newline at end of file +} diff --git a/cmd/stepsBaseInstall.go b/cmd/stepsBaseInstall.go index 1ac41f01a..5b1ae3d92 100644 --- a/cmd/stepsBaseInstall.go +++ b/cmd/stepsBaseInstall.go @@ -76,4 +76,4 @@ func destroyBaseTerraform(){ } else { log.Println("skip: destroyBaseTerraform") } -} \ No newline at end of file +} From 893b5d75615ef53c25183bc3413394858c705bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 6 Jul 2022 16:13:36 -0300 Subject: [PATCH 038/107] refactor: move setup functions to configs, add more descriptive documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 5 +- cmd/info.go | 20 ++++++-- cmd/init.go | 5 +- configs/envvars.go | 23 ++++++++++ configs/flareFile.go | 15 ++++++ configs/kubefirstDirectory.go | 18 ++++++++ .../flare => internal/telemetry}/telemetry.go | 5 +- pkg/flare/aws.go | 31 ------------- pkg/flare/envvars.go | 46 ------------------- pkg/flare/flareFile.go | 39 ---------------- pkg/flare/kubefirstDirectory.go | 40 ---------------- pkg/flare/progressTracker.go | 19 ++++---- 12 files changed, 88 insertions(+), 178 deletions(-) create mode 100755 configs/envvars.go create mode 100755 configs/flareFile.go create mode 100755 configs/kubefirstDirectory.go rename {pkg/flare => internal/telemetry}/telemetry.go (93%) delete mode 100644 pkg/flare/aws.go delete mode 100755 pkg/flare/envvars.go delete mode 100755 pkg/flare/flareFile.go delete mode 100755 pkg/flare/kubefirstDirectory.go diff --git a/cmd/create.go b/cmd/create.go index eeaa94234..8f06b9c14 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/internal/telemetry" "github.com/kubefirst/nebulous/pkg/flare" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -39,7 +40,7 @@ to quickly create a Cobra application.`, metricDomain := viper.GetString("aws.domainname") if !dryrunMode { - flare.SendTelemetry(metricDomain, metricName) + telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } @@ -88,7 +89,7 @@ to quickly create a Cobra application.`, metricName = "kubefirst.mgmt_cluster_install.completed" if !dryrunMode { - flare.SendTelemetry(metricDomain, metricName) + telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } diff --git a/cmd/info.go b/cmd/info.go index 4db172863..3e276f0e3 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -6,8 +6,9 @@ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/configs" "github.com/spf13/cobra" - "github.com/kubefirst/nebulous/pkg/flare" + "log" ) // infoCmd represents the info command @@ -17,7 +18,7 @@ var infoCmd = &cobra.Command{ Long: `Command used to allow a deeper inspection of the host machine and cli version runnig and its current state. Tool recommended for troubleshooting installations`, - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, args []string) { fmt.Printf("flare-cli golang utility version: v%s \n", NebolousVersion) fmt.Printf("OS type: %s\n", localOs) fmt.Printf("Arch: %s\n", localArchitecture) @@ -25,9 +26,18 @@ var infoCmd = &cobra.Command{ fmt.Printf("kubectl used: %s\n", kubectlClientPath) fmt.Printf("terraform used: %s\n", terraformPath) fmt.Printf("Kubeconfig in use: %s\n", kubeconfigPath) - flare.CheckFlareFile(home,true) - flare.CheckKubefirstDir(home,true) - flare.CheckEnvironment(true) + err := configs.CheckFlareFile(home) + if err != nil { + log.Panic(err) + } + err = configs.CheckKubefirstDir(home) + if err != nil { + log.Panic(err) + } + err = configs.CheckEnvironment() + if err != nil { + log.Panic(err) + } fmt.Printf("----------- \n") }, } diff --git a/cmd/init.go b/cmd/init.go index 6093357d2..5de97a6e3 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -7,6 +7,7 @@ package cmd import ( "fmt" "github.com/kubefirst/nebulous/internal/gitlab" + "github.com/kubefirst/nebulous/internal/telemetry" "github.com/kubefirst/nebulous/pkg/flare" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -59,7 +60,7 @@ to quickly create a Cobra application.`, metricName := "kubefirst.init.started" metricDomain := hostedZoneName if !dryrunMode { - flare.SendTelemetry(metricDomain, metricName) + telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } @@ -136,7 +137,7 @@ to quickly create a Cobra application.`, metricName = "kubefirst.init.completed" if !dryrunMode { - flare.SendTelemetry(metricDomain, metricName) + telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } diff --git a/configs/envvars.go b/configs/envvars.go new file mode 100755 index 000000000..927c15d1e --- /dev/null +++ b/configs/envvars.go @@ -0,0 +1,23 @@ +package configs + +import ( + "fmt" + "os" +) + +// CheckEnvironment validate if the required environment variable values are set. +func CheckEnvironment() error { + + requiredEnvValues := map[string]string{ + "AWS_PROFILE": os.Getenv("AWS_PROFILE"), + "AWS_REGION": os.Getenv("AWS_REGION"), + } + + for k, v := range requiredEnvValues { + if v == "" { + return fmt.Errorf("%s is not set", k) + } + } + + return nil +} \ No newline at end of file diff --git a/configs/flareFile.go b/configs/flareFile.go new file mode 100755 index 000000000..a74772d92 --- /dev/null +++ b/configs/flareFile.go @@ -0,0 +1,15 @@ +package configs + +import ( + "fmt" + "os" +) + +// CheckFlareFile validate if ~/.flare file is ready to be consumed. +func CheckFlareFile(home string) error { + flareFile := fmt.Sprintf("%s/.flare", home) + if _, err := os.Stat(flareFile); err != nil { + return fmt.Errorf("unable to load \".flare\" file, error is: %s", err) + } + return nil +} \ No newline at end of file diff --git a/configs/kubefirstDirectory.go b/configs/kubefirstDirectory.go new file mode 100755 index 000000000..70b68ed5e --- /dev/null +++ b/configs/kubefirstDirectory.go @@ -0,0 +1,18 @@ +package configs + +import ( + "fmt" + "log" + "os" +) + +// CheckKubefirstDir validate if ~/.kubefirst directory is ready to be used +func CheckKubefirstDir(home string) error { + k1sDir := fmt.Sprintf("%s/.kubefirst", home) + if _, err := os.Stat(k1sDir); err != nil { + return fmt.Errorf("unable to load \".kubefirst\" directory, error is: %s", err) + } + + log.Printf("\".kubefirst\" file found: %s", k1sDir) + return nil +} diff --git a/pkg/flare/telemetry.go b/internal/telemetry/telemetry.go similarity index 93% rename from pkg/flare/telemetry.go rename to internal/telemetry/telemetry.go index ee138f1ed..eebffe905 100644 --- a/pkg/flare/telemetry.go +++ b/internal/telemetry/telemetry.go @@ -1,13 +1,14 @@ -package flare +package telemetry import ( "fmt" - "log" "io/ioutil" + "log" "net/http" "strings" ) +// SendTelemetry post telemetry data func SendTelemetry(domain, metricName string) { url := "https://metaphor-go-production.kubefirst.io/telemetry" diff --git a/pkg/flare/aws.go b/pkg/flare/aws.go deleted file mode 100644 index 6d8aeed66..000000000 --- a/pkg/flare/aws.go +++ /dev/null @@ -1,31 +0,0 @@ -package flare - -import ( - "context" - - "log" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/eks" - "github.com/aws/aws-sdk-go/aws" -) - -func DescribeCluster() { - - cfg, err := config.LoadDefaultConfig(context.TODO()) - if err != nil { - log.Println("failed to load configuration, error:", err) - } - // https://aws.github.io/aws-sdk-go-v2/docs/making-requests/#overriding-configuration - eksClient := eks.NewFromConfig(cfg, func(o *eks.Options) { - o.Region = "us-east-2" - }) - - cluster, err := eksClient.DescribeCluster(context.TODO(), &eks.DescribeClusterInput{ - Name: aws.String("kubefirst"), - }) - if err != nil { - log.Println("error describing cluster", err) - } - // todo base64 encoded data : *cluster.Cluster.CertificateAuthority.Data, - log.Println("cluster:", *cluster.Cluster.Arn, *cluster.Cluster.Endpoint) -} diff --git a/pkg/flare/envvars.go b/pkg/flare/envvars.go deleted file mode 100755 index d8f0dd5d5..000000000 --- a/pkg/flare/envvars.go +++ /dev/null @@ -1,46 +0,0 @@ -package flare - -import ( - "log" - "fmt" - "os" - ) - -//Verify the state of the ".flare" file used to config provisioning. -// -// Output: -// $PATH/.flare -func CheckEnvironment(printOut bool) bool { - - if value := os.Getenv("AWS_REGION"); value == "" { - log.Printf("AWS_REGION env var not set.") - log.Printf("AWS_REGION is recommended for execution.") - if printOut { - fmt.Printf("AWS_REGION env var not set.\n") - fmt.Printf("AWS_REGION is recommended for execution.\n") - } - } else { - log.Printf("AWS_REGION env var set: %s",value) - if printOut { - fmt.Printf("AWS_REGION env var set: %s\n",value) - } - } - - if value := os.Getenv("AWS_PROFILE"); value == "" { - log.Printf("AWS_PROFILE env var not set.") - log.Printf("AWS_PROFILE is recommended for execution.") - if printOut { - log.Printf("AWS_PROFILE env var not set. \n") - log.Printf("AWS_PROFILE is recommended for execution.\n") - } - } else { - log.Printf("AWS_PROFILE env var set: %s",value) - if printOut { - log.Printf("AWS_PROFILE env var set: %s\n",value) - } - } - - - - return true -} \ No newline at end of file diff --git a/pkg/flare/flareFile.go b/pkg/flare/flareFile.go deleted file mode 100755 index 2a37e9252..000000000 --- a/pkg/flare/flareFile.go +++ /dev/null @@ -1,39 +0,0 @@ -package flare - -import ( - "log" - "fmt" - "os" - "errors" - ) - -//Verify the state of the ".flare" file used to config provisioning. -// -// Output: -// $PATH/.flare -func CheckFlareFile(home string, printOut bool) string { - flareFile := fmt.Sprintf("%s/.flare", home) - if _, err := os.Stat(flareFile); err == nil { - // path/to/whatever exists - log.Printf("\".flare\" file found: %s", flareFile) - if printOut { - fmt.Printf("\".flare\" file found: %s \n", flareFile) - } - } else if errors.Is(err, os.ErrNotExist) { - // path/to/whatever does *not* exist - log.Printf("\".flare\" file not found: %s", flareFile) - log.Printf(" \".flare\" is needed to guide installation process" ) - if printOut { - fmt.Printf("\".flare\" file not found: %s\n", flareFile) - fmt.Printf(" \".flare\" is needed to guide installation process\n" ) - } - } else { - // Schrodinger: file may or may not exist. See err for details. - // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence - log.Printf("Unable to check is \".flare\" if file exists" ) - if printOut { - fmt.Printf("Unable to check is \".flare\" if file exists\n" ) - } - } - return flareFile -} \ No newline at end of file diff --git a/pkg/flare/kubefirstDirectory.go b/pkg/flare/kubefirstDirectory.go deleted file mode 100755 index 7217ca57a..000000000 --- a/pkg/flare/kubefirstDirectory.go +++ /dev/null @@ -1,40 +0,0 @@ -package flare - -import ( - "log" - "fmt" - "os" - "errors" - ) - - -//Verify the state of the kubefirst directory -// -// Output: -// $PATH/.kubefirst -func CheckKubefirstDir(home string, printOut bool) string { - k1sDir := fmt.Sprintf("%s/.kubefirst", home) - if _, err := os.Stat(k1sDir); err == nil { - // path/to/whatever exists - log.Printf("\".kubefirst\" file found: %s", k1sDir) - log.Printf(" \".kubefirst\" will be generated by installation process, if exist means a installation may already be executed" ) - if printOut { - fmt.Printf("\".kubefirst\" file found: %s\n", k1sDir) - fmt.Printf(" \".kubefirst\" will be generated by installation process, if exist means a installation may already be executed\n" ) - } - } else if errors.Is(err, os.ErrNotExist) { - // path/to/whatever does *not* exist - log.Printf("\".kubefirst\" file not found: %s", k1sDir) - if printOut { - fmt.Printf("\".kubefirst\" file not found: %s\n", k1sDir) - } - } else { - // Schrodinger: file may or may not exist. See err for details. - // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence - log.Printf("Unable to check is \".kubefirst\" if file exists" ) - if printOut { - fmt.Printf("Unable to check is \".kubefirst\" if file exists\n" ) - } - } - return k1sDir -} \ No newline at end of file diff --git a/pkg/flare/progressTracker.go b/pkg/flare/progressTracker.go index bb94065da..f1de4e687 100644 --- a/pkg/flare/progressTracker.go +++ b/pkg/flare/progressTracker.go @@ -1,17 +1,14 @@ package flare import ( - "github.com/jedib0t/go-pretty/v6/progress" - "github.com/jedib0t/go-pretty/v6/text" "flag" "fmt" + "github.com/jedib0t/go-pretty/v6/progress" + "github.com/jedib0t/go-pretty/v6/text" "time" ) type ActionTracker struct { - //Message string - //Total int64 - //Increment int64 Tracker *progress.Tracker } @@ -23,9 +20,9 @@ var ( flagHidePercentage = flag.Bool("hide-percentage", false, "Hide the progress percent?") flagHideTime = flag.Bool("hide-time", false, "Hide the time taken?") flagHideValue = flag.Bool("hide-value", false, "Hide the tracker value?") -// flagNumTrackers = flag.Int("num-trackers", 12, "Number of Trackers") - flagRandomFail = flag.Bool("rnd-fail", false, "Introduce random failures in tracking") - flagRandomLogs = flag.Bool("rnd-logs", false, "Output random logs in the middle of tracking") + // flagNumTrackers = flag.Int("num-trackers", 12, "Number of Trackers") + flagRandomFail = flag.Bool("rnd-fail", false, "Introduce random failures in tracking") + flagRandomLogs = flag.Bool("rnd-logs", false, "Output random logs in the middle of tracking") messageColors = []text.Color{ text.FgRed, @@ -39,7 +36,7 @@ var ( ) var pw progress.Writer -func SetupProgress(numTrackers int){ +func SetupProgress(numTrackers int) { flagNumTrackers := flag.Int("num-trackers", numTrackers, "Number of Trackers") flag.Parse() fmt.Printf("Init actions: %d expected tasks ...\n\n", *flagNumTrackers) @@ -65,10 +62,10 @@ func SetupProgress(numTrackers int){ } -func CreateTracker(title string, total int64) *progress.Tracker{ +func CreateTracker(title string, total int64) *progress.Tracker { units := &progress.UnitsDefault message := title tracker := progress.Tracker{Message: message, Total: total, Units: *units} pw.AppendTracker(&tracker) return &tracker -} \ No newline at end of file +} From 00ef485ce3f29e4240cc1b4d9ab648e457ba476e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 6 Jul 2022 16:22:09 -0300 Subject: [PATCH 039/107] chore: add better log data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- configs/envvars.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/configs/envvars.go b/configs/envvars.go index 927c15d1e..eda3562c9 100755 --- a/configs/envvars.go +++ b/configs/envvars.go @@ -2,6 +2,7 @@ package configs import ( "fmt" + "log" "os" ) @@ -15,9 +16,13 @@ func CheckEnvironment() error { for k, v := range requiredEnvValues { if v == "" { - return fmt.Errorf("%s is not set", k) + errorMsg := fmt.Sprintf("%s is not set", k) + log.Printf(errorMsg) + return fmt.Errorf(errorMsg) } } + log.Println("all environment variables are set") + return nil -} \ No newline at end of file +} From 19d16d45d3d69c5b1da26333020ff0127b40b821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 6 Jul 2022 16:24:26 -0300 Subject: [PATCH 040/107] chore: add better log data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- configs/flareFile.go | 7 ++++++- configs/kubefirstDirectory.go | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/configs/flareFile.go b/configs/flareFile.go index a74772d92..6229dbd0b 100755 --- a/configs/flareFile.go +++ b/configs/flareFile.go @@ -2,6 +2,7 @@ package configs import ( "fmt" + "log" "os" ) @@ -9,7 +10,11 @@ import ( func CheckFlareFile(home string) error { flareFile := fmt.Sprintf("%s/.flare", home) if _, err := os.Stat(flareFile); err != nil { - return fmt.Errorf("unable to load \".flare\" file, error is: %s", err) + errorMsg := fmt.Sprintf("unable to load \".flare\" file, error is: %s", err) + log.Println(errorMsg) + return fmt.Errorf(errorMsg) } + + log.Println(".flare file is set") return nil } \ No newline at end of file diff --git a/configs/kubefirstDirectory.go b/configs/kubefirstDirectory.go index 70b68ed5e..bdf0146d0 100755 --- a/configs/kubefirstDirectory.go +++ b/configs/kubefirstDirectory.go @@ -10,7 +10,9 @@ import ( func CheckKubefirstDir(home string) error { k1sDir := fmt.Sprintf("%s/.kubefirst", home) if _, err := os.Stat(k1sDir); err != nil { - return fmt.Errorf("unable to load \".kubefirst\" directory, error is: %s", err) + errorMsg := fmt.Sprintf("unable to load \".flare\" file, error is: %s", err) + log.Println(errorMsg) + return fmt.Errorf(errorMsg) } log.Printf("\".kubefirst\" file found: %s", k1sDir) From 8b866ffe8716bee08b0b71c84f44b15d918073ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 15:28:02 -0300 Subject: [PATCH 041/107] refactor: add initial single source of truth for config, re-structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/checktools.go | 18 +- cmd/clean.go | 7 +- cmd/create.go | 46 ++-- cmd/destroy.go | 25 ++- cmd/gitlab.go | 9 +- cmd/globals.go | 27 +-- cmd/info.go | 44 ++-- cmd/init.go | 137 ++++++------ cmd/kubefirstTemplate.go | 24 +-- cmd/root.go | 20 +- cmd/stepsArgo.go | 132 ++++++------ cmd/stepsBaseInstall.go | 32 +-- cmd/stepsMetaphor.go | 16 +- cmd/stepsSoftServe.go | 42 ++-- cmd/stepsVault.go | 23 +- cmd/version.go | 8 +- cmd/versionConstant.go | 5 - configs/config.go | 64 ++++++ configs/kubefirstDirectory.go | 2 +- go.mod | 2 +- go.sum | 4 +- {cmd => internal/aws}/aws.go | 70 +++--- .../downloadManager/download.go | 204 +++++++++++------- main.go | 26 +-- .../progressTracker.go => progress_bar.go} | 62 +++--- {cmd => pkg}/shell.go | 8 +- 26 files changed, 606 insertions(+), 451 deletions(-) delete mode 100644 cmd/versionConstant.go create mode 100644 configs/config.go rename {cmd => internal/aws}/aws.go (89%) rename cmd/files.go => internal/downloadManager/download.go (52%) rename pkg/{flare/progressTracker.go => progress_bar.go} (62%) rename {cmd => pkg}/shell.go (80%) diff --git a/cmd/checktools.go b/cmd/checktools.go index 29ba8011c..7731990f5 100644 --- a/cmd/checktools.go +++ b/cmd/checktools.go @@ -6,6 +6,8 @@ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" ) @@ -17,14 +19,16 @@ var checktoolsCmd = &cobra.Command{ Execute After callint "init". If executed before init, tools will not be available. `, Run: func(cmd *cobra.Command, args []string) { + config := configs.ReadConfig() + fmt.Println("Checking the tools installed used by installer:") - kubectlVersion, kubectlStdErr,errKubectl := execShellReturnStrings(kubectlClientPath, "version", "--client", "--short") - fmt.Printf("-> kubectl version:\n\t%s\n\t%s\n",kubectlVersion,kubectlStdErr) - terraformVersion, terraformStdErr,errTerraform := execShellReturnStrings(terraformPath, "version") - fmt.Printf("-> terraform version:\n\t%s\n\t%s\n",terraformVersion,terraformStdErr) - helmVersion, helmStdErr,errHelm := execShellReturnStrings(helmClientPath, "version", "--client", "--short") - fmt.Printf("-> helm version:\n\t%s\n\t%s\n",helmVersion,helmStdErr) + kubectlVersion, kubectlStdErr, errKubectl := pkg.ExecShellReturnStrings(config.KubectlClientPath, "version", "--client", "--short") + fmt.Printf("-> kubectl version:\n\t%s\n\t%s\n", kubectlVersion, kubectlStdErr) + terraformVersion, terraformStdErr, errTerraform := pkg.ExecShellReturnStrings(config.TerraformPath, "version") + fmt.Printf("-> terraform version:\n\t%s\n\t%s\n", terraformVersion, terraformStdErr) + helmVersion, helmStdErr, errHelm := pkg.ExecShellReturnStrings(config.HelmClientPath, "version", "--client", "--short") + fmt.Printf("-> helm version:\n\t%s\n\t%s\n", helmVersion, helmStdErr) if errKubectl != nil { fmt.Println("failed to call kubectlVersionCmd.Run(): %v", errKubectl) @@ -35,7 +39,7 @@ var checktoolsCmd = &cobra.Command{ if errTerraform != nil { fmt.Println("failed to call terraformVersionCmd.Run(): %v", errTerraform) } - + }, } diff --git a/cmd/clean.go b/cmd/clean.go index fc6aa3fe4..fe5305aeb 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -6,6 +6,7 @@ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/configs" "log" "os" @@ -23,10 +24,12 @@ Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { + config := configs.ReadConfig() + log.Println("removing $HOME/.kubefirst and $HOME/.flare") // todo ask for user input to verify? - os.RemoveAll(fmt.Sprintf("%s/.kubefirst", home)) - os.Remove(fmt.Sprintf("%s/.flare", home)) + os.RemoveAll(fmt.Sprintf("%s/.kubefirst", config.HomePath)) + os.Remove(fmt.Sprintf("%s/.flare", config.HomePath)) log.Println("removed $HOME/.kubefirst and $HOME/.flare") // todo log.Println("proceed to kubefirst create ") log.Println("proceed to flare nebulous create ") diff --git a/cmd/create.go b/cmd/create.go index 99cbf6d1e..cc681a4a9 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -2,8 +2,9 @@ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/internal/telemetry" - "github.com/kubefirst/nebulous/pkg/flare" + "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" "log" @@ -14,6 +15,7 @@ const trackerStage20 = "0 - Apply Base" const trackerStage21 = "1 - Temporary SCM Install" const trackerStage22 = "2 - Argo/Final SCM Install" const trackerStage23 = "3 - Final Setup" + var skipVault bool var skipGitlab bool @@ -29,45 +31,48 @@ This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { - flare.SetupProgress(4) - Trackers = make(map[string]*flare.ActionTracker) - Trackers[trackerStage20] = &flare.ActionTracker{Tracker: flare.CreateTracker(trackerStage20, int64(1))} - Trackers[trackerStage21] = &flare.ActionTracker{Tracker: flare.CreateTracker(trackerStage21, int64(2))} - Trackers[trackerStage22] = &flare.ActionTracker{Tracker: flare.CreateTracker(trackerStage22, int64(7))} - Trackers[trackerStage23] = &flare.ActionTracker{Tracker: flare.CreateTracker(trackerStage23, int64(3))} + config := configs.ReadConfig() + + pkg.SetupProgress(4) + Trackers := make(map[string]*pkg.ActionTracker) + + Trackers[trackerStage20] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage20, 1)} + Trackers[trackerStage21] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage21, 2)} + Trackers[trackerStage22] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage22, 7)} + Trackers[trackerStage23] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage23, 3)} infoCmd.Run(cmd, args) metricName := "kubefirst.mgmt_cluster_install.started" metricDomain := viper.GetString("aws.domainname") - if !dryrunMode { + if !config.DryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) applyBaseTerraform(cmd, directory) Trackers[trackerStage20].Tracker.Increment(int64(1)) - createSoftServe(kubeconfigPath) + createSoftServe(config.KubeConfigPath) Trackers[trackerStage21].Tracker.Increment(int64(1)) configureSoftserveAndPush() Trackers[trackerStage21].Tracker.Increment(int64(1)) - helmInstallArgocd(home, kubeconfigPath) + helmInstallArgocd(config.HomePath) Trackers[trackerStage22].Tracker.Increment(int64(1)) if !skipGitlab { //TODO: Confirm if we need to waitgit lab to be ready // OR something, too fast the secret will not be there. - awaitGitlab() + awaitGitlab() produceGitlabTokens() - Trackers[trackerStage22].Tracker.Increment(int64(1)) + Trackers[trackerStage22].Tracker.Increment(int64(1)) applyGitlabTerraform(directory) Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlabKeyUpload() Trackers[trackerStage22].Tracker.Increment(int64(1)) - + if !skipVault { configureVault() Trackers[trackerStage23].Tracker.Increment(int64(1)) @@ -75,17 +80,15 @@ to quickly create a Cobra application.`, Trackers[trackerStage23].Tracker.Increment(int64(1)) awaitGitlab() Trackers[trackerStage22].Tracker.Increment(int64(1)) - + pushGitopsToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) changeRegistryToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) - - - + hydrateGitlabMetaphorRepo() Trackers[trackerStage23].Tracker.Increment(int64(1)) - + token := getArgocdAuthToken() syncArgocdApplication("argo-components", token) syncArgocdApplication("gitlab-runner-components", token) @@ -97,7 +100,7 @@ to quickly create a Cobra application.`, metricName = "kubefirst.mgmt_cluster_install.completed" - if !dryrunMode { + if !config.DryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) @@ -107,11 +110,12 @@ to quickly create a Cobra application.`, } func init() { + config := configs.ReadConfig() rootCmd.AddCommand(createCmd) // todo: make this an optional switch and check for it or viper createCmd.Flags().Bool("destroy", false, "destroy resources") - createCmd.PersistentFlags().BoolVarP(&dryrunMode, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") + createCmd.PersistentFlags().BoolVarP(&config.DryRun, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") createCmd.PersistentFlags().BoolVar(&skipVault, "skip-vault", false, "Skip post-git lab install and vault setup") createCmd.PersistentFlags().BoolVar(&skipGitlab, "skip-gitlab", false, "Skip git lab install and vault setup") diff --git a/cmd/destroy.go b/cmd/destroy.go index 8d643711a..b1c2f0c1a 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -6,18 +6,21 @@ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/aws" + "github.com/spf13/cobra" + "github.com/spf13/viper" "log" "os" "os/exec" "syscall" - "github.com/spf13/cobra" - "github.com/spf13/viper" ) var skipGitlabTerraform bool -var skipDeleteRegistryApplication bool -var skipBaseTerraform bool -var destroyBuckets bool +var skipDeleteRegistryApplication bool +var skipBaseTerraform bool +var DestroyBuckets bool + // destroyCmd represents the destroy command var destroyCmd = &cobra.Command{ Use: "destroy", @@ -29,7 +32,9 @@ Optional: skip gitlab terraform if the registry has already been delteted.`, Run: func(cmd *cobra.Command, args []string) { - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + config := configs.ReadConfig() + + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr err := kPortForward.Start() @@ -43,7 +48,7 @@ if the registry has already been delteted.`, deleteRegistryApplication() destroyBaseTerraform() //TODO: Remove buckets? Opt-in flag - destroyBucketsInUse() + aws.DestroyBucketsInUse() }, } @@ -54,14 +59,14 @@ func init() { destroyCmd.PersistentFlags().BoolVar(&skipGitlabTerraform, "skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") destroyCmd.PersistentFlags().BoolVar(&skipDeleteRegistryApplication, "skip-delete-register", false, "whether to skip deletion of resgister application ") destroyCmd.PersistentFlags().BoolVar(&skipBaseTerraform, "skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") - destroyCmd.PersistentFlags().BoolVar(&destroyBuckets, "destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") + destroyCmd.PersistentFlags().BoolVar(&DestroyBuckets, "destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") } - func deleteRegistryApplication() { + config := configs.ReadConfig() if !skipDeleteRegistryApplication { log.Println("starting port forward to argocd server and deleting registry") - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr err := kPortForward.Start() diff --git a/cmd/gitlab.go b/cmd/gitlab.go index b9a453728..b0ed0fd8d 100644 --- a/cmd/gitlab.go +++ b/cmd/gitlab.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "encoding/json" "fmt" + "github.com/kubefirst/nebulous/configs" "log" "net/http" "net/url" @@ -16,7 +17,9 @@ import ( ) func gitlabGeneratePersonalAccessToken(gitlabPodName string) { - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + config := configs.ReadConfig() + + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr err := kPortForward.Start() @@ -30,7 +33,7 @@ func gitlabGeneratePersonalAccessToken(gitlabPodName string) { id := uuid.New() gitlabToken := id.String()[:20] - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) k.Stdout = os.Stdout k.Stderr = os.Stderr err = k.Run() @@ -62,4 +65,4 @@ func uploadGitlabSSHKey(gitlabToken string) { log.Println("ssh public key uploaded to gitlab") viper.Set("gitlab.keyuploaded", true) viper.WriteConfig() -} \ No newline at end of file +} diff --git a/cmd/globals.go b/cmd/globals.go index d16f5e341..d8a3f6cab 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -1,29 +1,8 @@ - package cmd +package cmd -import ( - "fmt" - "log" - "os" - "runtime" -) //Common used strings by all commands -var home, kubectlClientPath, kubeconfigPath,localOs,localArchitecture,terraformPath,helmClientPath string -var dryrunMode bool +// todo: remove after Config (config package) is ready +//var home, kubectlClientPath, kubeconfigPath, localOs, localArchitecture, terraformPath, helmClientPath string //Should this be loaded from somewhere? var installerEmail = "kubefirst-bot@kubefirst.com" -//setGlobals for all common used properties -func setGlobals() { - tmphome, err := os.UserHomeDir() - home = tmphome - if(err != nil){ - log.Panicf("Error Defining home - %s", err) - } - localOs = runtime.GOOS - localArchitecture = runtime.GOARCH - kubectlClientPath = fmt.Sprintf("%s/.kubefirst/tools/kubectl", home) - kubeconfigPath = fmt.Sprintf("%s/.kubefirst/gitops/terraform/base/kubeconfig_kubefirst", home) - terraformPath = fmt.Sprintf("%s/.kubefirst/tools/terraform", home) - helmClientPath = fmt.Sprintf("%s/.kubefirst/tools/helm", home) - dryrunMode = false -} \ No newline at end of file diff --git a/cmd/info.go b/cmd/info.go index 3e276f0e3..84d7649d8 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -1,7 +1,3 @@ -/* -Copyright © 2022 NAME HERE - -*/ package cmd import ( @@ -9,28 +5,32 @@ import ( "github.com/kubefirst/nebulous/configs" "github.com/spf13/cobra" "log" + "runtime" ) // infoCmd represents the info command var infoCmd = &cobra.Command{ Use: "info", - Short: "Provide a general overview of host machine and cli", - Long: `Command used to allow a deeper inspection of the host machine - and cli version runnig and its current state. Tool recommended for troubleshooting - installations`, + Short: "Provides general Kubefirst setup data", + Long: `Provides machine data, files and folders paths`, Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("flare-cli golang utility version: v%s \n", NebolousVersion) - fmt.Printf("OS type: %s\n", localOs) - fmt.Printf("Arch: %s\n", localArchitecture) - fmt.Printf("$HOME folder: %s\n", home) - fmt.Printf("kubectl used: %s\n", kubectlClientPath) - fmt.Printf("terraform used: %s\n", terraformPath) - fmt.Printf("Kubeconfig in use: %s\n", kubeconfigPath) - err := configs.CheckFlareFile(home) + + config := configs.ReadConfig() + + fmt.Printf("Kubefirst CLI version: v%s \n", config.KubefirstVersion) + fmt.Printf("Operational System: %s\n", config.LocalOs) + fmt.Printf("Architecture: %s\n", config.LocalArchitecture) + fmt.Printf("Go Lang version: v%s \n", runtime.Version()) + fmt.Printf("$HOME folder: %s\n", config.HomePath) + fmt.Printf("Kubectl path: %s\n", config.KubectlClientPath) + fmt.Printf("Terraform path: %s\n", config.TerraformPath) + fmt.Printf("Kubeconfig path: %s\n", config.KubeConfigPath) + + err := configs.CheckFlareFile(config.HomePath) if err != nil { log.Panic(err) } - err = configs.CheckKubefirstDir(home) + err = configs.CheckKubefirstDir(config.HomePath) if err != nil { log.Panic(err) } @@ -44,14 +44,4 @@ var infoCmd = &cobra.Command{ func init() { rootCmd.AddCommand(infoCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // infoCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // infoCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } diff --git a/cmd/init.go b/cmd/init.go index 5de97a6e3..b25ff87a1 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -1,36 +1,21 @@ -/* -Copyright © 2022 NAME HERE - -*/ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/aws" + "github.com/kubefirst/nebulous/internal/downloadManager" "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/telemetry" - "github.com/kubefirst/nebulous/pkg/flare" + "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" "io/ioutil" "log" "strings" "time" - //gitlabSsh "github.com/kubefirst/nebulous/pkg/ssh" ) -var Trackers map[string]*flare.ActionTracker - -const trackerStage0 = "1 - Load properties" -const trackerStage1 = "2 - Set .flare initial values" -const trackerStage2 = "3 - Test Domain Liveness" -const trackerStage3 = "4 - Create SSH Key Pair" -const trackerStage4 = "5 - Load Templates" -const trackerStage5 = "6 - Download Tools" -const trackerStage6 = "7 - Get Account Info" -const trackerStage7 = "8 - Create Buckets" -const trackerStage8 = "9 - Detokenize" -const trackerStage9 = "10 - Send Telemetry" - // initCmd represents the init command var initCmd = &cobra.Command{ Use: "init", @@ -43,23 +28,38 @@ This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { - flare.SetupProgress(10) - Trackers = make(map[string]*flare.ActionTracker) - Trackers[trackerStage0] = &flare.ActionTracker{flare.CreateTracker(trackerStage0, int64(1))} - Trackers[trackerStage1] = &flare.ActionTracker{flare.CreateTracker(trackerStage1, int64(1))} - Trackers[trackerStage2] = &flare.ActionTracker{flare.CreateTracker(trackerStage2, int64(1))} - Trackers[trackerStage3] = &flare.ActionTracker{flare.CreateTracker(trackerStage3, int64(1))} - Trackers[trackerStage4] = &flare.ActionTracker{flare.CreateTracker(trackerStage4, int64(1))} - Trackers[trackerStage5] = &flare.ActionTracker{flare.CreateTracker(trackerStage5, int64(3))} - Trackers[trackerStage6] = &flare.ActionTracker{flare.CreateTracker(trackerStage6, int64(1))} - Trackers[trackerStage7] = &flare.ActionTracker{flare.CreateTracker(trackerStage7, int64(4))} - Trackers[trackerStage8] = &flare.ActionTracker{flare.CreateTracker(trackerStage8, int64(1))} - Trackers[trackerStage9] = &flare.ActionTracker{flare.CreateTracker(trackerStage9, int64(1))} + config := configs.ReadConfig() + + var err error + config.DryRun, err = cmd.Flags().GetBool("dry-run") + if err != nil { + panic(err) + } + + log.Println("dry run enabled:", config.DryRun) + + pkg.SetupProgress(10) + trackers := pkg.GetTrackers() + trackers[pkg.TrackerStage0] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage0, 1)} + trackers[pkg.TrackerStage1] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage1, 1)} + trackers[pkg.TrackerStage2] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage2, 1)} + trackers[pkg.TrackerStage3] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage3, 1)} + trackers[pkg.TrackerStage4] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage4, 1)} + trackers[pkg.TrackerStage5] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage5, 3)} + trackers[pkg.TrackerStage6] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage6, 1)} + trackers[pkg.TrackerStage7] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage7, 4)} + trackers[pkg.TrackerStage8] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage8, 1)} + trackers[pkg.TrackerStage9] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage9, 1)} infoCmd.Run(cmd, args) hostedZoneName, _ := cmd.Flags().GetString("hosted-zone-name") metricName := "kubefirst.init.started" metricDomain := hostedZoneName - if !dryrunMode { + + log.Println("---debug---") + log.Println(config.DryRun) + log.Println("---debug---") + + if !config.DryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) @@ -90,86 +90,103 @@ to quickly create a Cobra application.`, // hosted zone id // so we don't have to keep looking it up from the domain name to use it - hostedZoneId := getDNSInfo(hostedZoneName) + hostedZoneId := aws.GetDNSInfo(hostedZoneName) // viper values set in above function log.Println("hostedZoneId:", hostedZoneId) - Trackers[trackerStage0].Tracker.Increment(int64(1)) - Trackers[trackerStage1].Tracker.Increment(int64(1)) - //trackProgress(1, false) + trackers[pkg.TrackerStage0].Tracker.Increment(1) + trackers[pkg.TrackerStage1].Tracker.Increment(1) + // todo: this doesn't default to testing the dns check skipHostedZoneCheck := viper.GetBool("init.hostedzonecheck.enabled") if !skipHostedZoneCheck { log.Println("skipping hosted zone check") } else { - testHostedZoneLiveness(hostedZoneName, hostedZoneId) + aws.TestHostedZoneLiveness(hostedZoneName, hostedZoneId) } - Trackers[trackerStage2].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage2].Tracker.Increment(1) log.Println("calling createSshKeyPair() ") createSshKeyPair() log.Println("createSshKeyPair() complete") - Trackers[trackerStage3].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage3].Tracker.Increment(1) log.Println("calling cloneGitOpsRepo()") cloneGitOpsRepo() log.Println("cloneGitOpsRepo() complete") - Trackers[trackerStage4].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage4].Tracker.Increment(1) log.Println("calling download()") - download() + err = downloadManager.DownloadTools(config, trackers) + if err != nil { + panic(err) + } + log.Println("download() complete") - log.Println("calling getAccountInfo()") - getAccountInfo() - log.Println("getAccountInfo() complete") - Trackers[trackerStage6].Tracker.Increment(int64(1)) + log.Println("calling GetAccountInfo()") + aws.GetAccountInfo() + log.Println("GetAccountInfo() complete") + trackers[pkg.TrackerStage6].Tracker.Increment(1) - log.Println("calling bucketRand()") - bucketRand() - log.Println("bucketRand() complete") + log.Println("calling BucketRand()") + aws.BucketRand() + log.Println("BucketRand() complete") log.Println("calling detokenize()") - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) log.Println("detokenize() complete") - Trackers[trackerStage8].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage8].Tracker.Increment(1) // modConfigYaml() metricName = "kubefirst.init.completed" - if !dryrunMode { + if !config.DryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } viper.WriteConfig() - Trackers[trackerStage9].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage9].Tracker.Increment(1) time.Sleep(time.Millisecond * 100) }, } func init() { + config := configs.ReadConfig() rootCmd.AddCommand(initCmd) - initCmd.Flags().String("hosted-zone-name", "", "the domain to provision the kubefirst platofrm in") - initCmd.MarkFlagRequired("hosted-zone-name") + initCmd.Flags().String("hosted-zone-name", "", "the domain to provision the kubefirst platform in") + err := initCmd.MarkFlagRequired("hosted-zone-name") + if err != nil { + panic(err) + } initCmd.Flags().String("admin-email", "", "the email address for the administrator as well as for lets-encrypt certificate emails") - initCmd.MarkFlagRequired("admin-email") + err = initCmd.MarkFlagRequired("admin-email") + if err != nil { + panic(err) + } initCmd.Flags().String("cloud", "", "the cloud to provision infrastructure in") - initCmd.MarkFlagRequired("cloud") + err = initCmd.MarkFlagRequired("cloud") + if err != nil { + panic(err) + } initCmd.Flags().String("region", "", "the region to provision the cloud resources in") - initCmd.MarkFlagRequired("region") + err = initCmd.MarkFlagRequired("region") + if err != nil { + panic(err) + } initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.flare") log.SetPrefix("LOG: ") log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) - initCmd.PersistentFlags().BoolVarP(&dryrunMode, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") + initCmd.PersistentFlags().BoolVarP(&config.DryRun, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") log.Println("init started") - } func createSshKeyPair() { + config := configs.ReadConfig() publicKey := viper.GetString("botpublickey") if publicKey == "" { log.Println("generating new key pair") @@ -221,7 +238,7 @@ configs: %s `, strings.ReplaceAll(privateKey, "\n", "\n "))) - err := ioutil.WriteFile(fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), argocdInitValuesYaml, 0644) + err := ioutil.WriteFile(fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", config.HomePath), argocdInitValuesYaml, 0644) if err != nil { log.Panicf("error: could not write argocd-init-values.yaml %s", err) } diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 9fad85e0f..2948121b6 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -2,25 +2,25 @@ package cmd import ( "fmt" - "log" - "os" - ssh2 "golang.org/x/crypto/ssh" "github.com/go-git/go-git/v5" gitConfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" - "path/filepath" - "strings" + "github.com/kubefirst/nebulous/configs" "github.com/spf13/viper" + ssh2 "golang.org/x/crypto/ssh" "io/ioutil" + "log" + "os" + "path/filepath" + "strings" "time" - - ) func cloneGitOpsRepo() { + config := configs.ReadConfig() url := "https://github.com/kubefirst/gitops-template" - directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) log.Println("git clone", url, directory) @@ -28,15 +28,16 @@ func cloneGitOpsRepo() { URL: url, }) if err != nil { - log.Panicf("reror cloning gitops-template repository from github %s", err) + log.Panicf("error cloning gitops-template repository from github, error is: %s", err) } - log.Println("downloaded gitops repo from template to directory", home, "/.kubefirst/gitops") + log.Println("downloaded gitops repo from template to directory", config.HomePath, "/.kubefirst/gitops") } func pushGitopsToSoftServe() { - directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + config := configs.ReadConfig() + directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) log.Println("open git repo", directory) @@ -79,7 +80,6 @@ func pushGitopsToSoftServe() { } - func detokenize(path string) { err := filepath.Walk(path, detokenizeDirectory) diff --git a/cmd/root.go b/cmd/root.go index d514ea5d0..66004511d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,15 +1,12 @@ -/* -Copyright © 2022 NAME HERE - -*/ package cmd import ( - "os" "errors" - "log" + "github.com/kubefirst/nebulous/configs" "github.com/spf13/cobra" "github.com/spf13/viper" + "log" + "os" ) var cfgFile string @@ -41,7 +38,6 @@ func Execute() { } func init() { - setGlobals() cobra.OnInitialize(initConfig) // Here you will define your flags and configuration settings. @@ -57,9 +53,9 @@ func init() { // initConfig reads in config file and ENV variables if set. func initConfig() { - + config := configs.ReadConfig() if cfgFile == "" { - cfgFile = home + "/.flare" + cfgFile = config.HomePath + "/.flare" } if _, err := os.Stat(cfgFile); errors.Is(err, os.ErrNotExist) { @@ -71,12 +67,12 @@ func initConfig() { } viper.SetConfigFile(cfgFile) - viper.SetConfigType("yaml") + viper.SetConfigType("yaml") viper.AutomaticEnv() // read in environment variables that match // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { - log.Println( "Using config file:", viper.ConfigFileUsed()) + log.Println("Using config file:", viper.ConfigFileUsed()) } - + } diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index 342d54222..58b0b175f 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -1,42 +1,45 @@ package cmd import ( - "fmt" - "log" - "os" - "strings" - "github.com/spf13/viper" - "syscall" - "os/exec" - "time" + "bytes" + "context" "crypto/tls" - "net/url" - "net/http" + b64 "encoding/base64" "encoding/json" - "io/ioutil" - "bytes" + "fmt" + "github.com/ghodss/yaml" "github.com/go-git/go-git/v5" gitConfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" - b64 "encoding/base64" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/pkg" + "github.com/spf13/viper" + "html/template" + "io/ioutil" v1 "k8s.io/api/core/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "html/template" - "github.com/ghodss/yaml" - "context" + "k8s.io/client-go/tools/clientcmd" + "log" + "net/http" + "net/url" + "os" + "os/exec" + "strings" + "syscall" + "time" ) -func helmInstallArgocd(home string, kubeconfigPath string) { +func helmInstallArgocd(home string) { + config := configs.ReadConfig() if !viper.GetBool("create.argocd.helm") { - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, helmInstallArgocd skipped.") return } // ! commenting out until a clean execution is necessary // create namespace - helmRepoAddArgocd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") + helmRepoAddArgocd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") helmRepoAddArgocd.Stdout = os.Stdout helmRepoAddArgocd.Stderr = os.Stderr err := helmRepoAddArgocd.Run() @@ -44,7 +47,7 @@ func helmInstallArgocd(home string, kubeconfigPath string) { log.Panicf("error: could not run helm repo add %s", err) } - helmRepoUpdate := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "repo", "update") + helmRepoUpdate := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "update") helmRepoUpdate.Stdout = os.Stdout helmRepoUpdate.Stderr = os.Stderr err = helmRepoUpdate.Run() @@ -52,7 +55,7 @@ func helmInstallArgocd(home string, kubeconfigPath string) { log.Panicf("error: could not helm repo update %s", err) } - helmInstallArgocdCmd := exec.Command(helmClientPath, "--kubeconfig", kubeconfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") + helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") helmInstallArgocdCmd.Stdout = os.Stdout helmInstallArgocdCmd.Stderr = os.Stderr err = helmInstallArgocdCmd.Run() @@ -69,11 +72,13 @@ func helmInstallArgocd(home string, kubeconfigPath string) { } func awaitGitlab() { + config := configs.ReadConfig() + log.Println("awaitGitlab called") - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, awaitGitlab skipped.") return - } + } max := 200 for i := 0; i < max; i++ { hostedZoneName := viper.GetString("aws.hostedzonename") @@ -89,18 +94,19 @@ func awaitGitlab() { } } -func produceGitlabTokens(){ +func produceGitlabTokens() { //TODO: Should this step be skipped if already executed? - config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + config := configs.ReadConfig() + k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) if err != nil { panic(err.Error()) } - clientset, err := kubernetes.NewForConfig(config) + clientset, err := kubernetes.NewForConfig(k8sConfig) if err != nil { panic(err.Error()) } - log.Println("discovering gitlab toolbox pod") - if dryrunMode { + log.Println("discovering gitlab toolbox pod") + if config.DryRun { log.Printf("[#99] Dry-run mode, produceGitlabTokens skipped.") return } @@ -155,28 +161,28 @@ func produceGitlabTokens(){ } -func applyGitlabTerraform(directory string){ +func applyGitlabTerraform(directory string) { + config := configs.ReadConfig() if !viper.GetBool("create.terraformapplied.gitlab") { log.Println("Executing applyGitlabTerraform") - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, applyGitlabTerraform skipped.") return - } + } // Prepare for terraform gitlab execution os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") - - directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) + directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) err := os.Chdir(directory) if err != nil { log.Panic("error: could not change directory to " + directory) } - _,_,errInit := execShellReturnStrings(terraformPath, "init") + _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") if errInit != nil { panic(fmt.Sprintf("error: terraform init for gitlab failed %s", err)) } - _,_,errApply := execShellReturnStrings(terraformPath, "apply", "-auto-approve") + _, _, errApply := pkg.ExecShellReturnStrings(config.TerraformPath, "apply", "-auto-approve") if errApply != nil { panic(fmt.Sprintf("error: terraform apply for gitlab failed %s", err)) } @@ -188,17 +194,16 @@ func applyGitlabTerraform(directory string){ } } - - -func gitlabKeyUpload(){ - // upload ssh public key +func gitlabKeyUpload() { + config := configs.ReadConfig() + // upload ssh public key if !viper.GetBool("gitlab.keyuploaded") { log.Println("Executing gitlabKeyUpload") log.Println("uploading ssh public key for gitlab user") - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, gitlabKeyUpload skipped.") return - } + } log.Println("uploading ssh public key to gitlab") gitlabToken := viper.GetString("gitlab.token") data := url.Values{ @@ -225,24 +230,25 @@ func gitlabKeyUpload(){ } func pushGitopsToGitLab() { - if dryrunMode { + config := configs.ReadConfig() + if config.DryRun { log.Printf("[#99] Dry-run mode, pushGitopsToGitLab skipped.") return } - + //TODO: should this step to be skipped if already executed? domain := viper.GetString("aws.hostedzonename") - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) - directory := fmt.Sprintf("%s/.kubefirst/gitops", home) + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) + directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) repo, err := git.PlainOpen(directory) if err != nil { log.Panicf("error opening the directory ", directory, err) } - //upstream := fmt.Sprintf("ssh://gitlab.%s:22:kubefirst/gitops", viper.GetString("aws.hostedzonename")) - // upstream := "git@gitlab.kube1st.com:kubefirst/gitops.git" + //upstream := fmt.Sprintf("ssh://gitlab.%s:22:kubefirstVersion/gitops", viper.GetString("aws.hostedzonename")) + // upstream := "git@gitlab.kube1st.com:kubefirstVersion/gitops.git" upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) log.Println("git remote add gitlab at url", upstream) @@ -292,8 +298,9 @@ func pushGitopsToGitLab() { } func changeRegistryToGitLab() { + config := configs.ReadConfig() if !viper.GetBool("gitlab.registry") { - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, changeRegistryToGitLab skipped.") return } @@ -311,11 +318,11 @@ func changeRegistryToGitLab() { creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} var argocdRepositoryAccessTokenSecret *v1.Secret - config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) if err != nil { log.Panicf("error getting client from kubeconfig") } - clientset, err := kubernetes.NewForConfig(config) + clientset, err := kubernetes.NewForConfig(k8sConfig) if err != nil { log.Panicf("error getting kubeconfig for clientset") } @@ -381,7 +388,7 @@ func changeRegistryToGitLab() { log.Panicf("error creating argocd repository connection secret %s", err) } - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", home)) + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.HomePath)) k.Stdout = os.Stdout k.Stderr = os.Stderr err = k.Run() @@ -397,11 +404,12 @@ func changeRegistryToGitLab() { } func getArgocdAuthToken() string { - if dryrunMode { + config := configs.ReadConfig() + if config.DryRun { log.Printf("[#99] Dry-run mode, getArgocdAuthToken skipped.") return "nothing" - } - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + } + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr err := kPortForward.Start() @@ -452,11 +460,12 @@ func getArgocdAuthToken() string { } func syncArgocdApplication(applicationName, argocdAuthToken string) { - if dryrunMode { + config := configs.ReadConfig() + if config.DryRun { log.Printf("[#99] Dry-run mode, syncArgocdApplication skipped.") return } - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr err := kPortForward.Start() @@ -478,7 +487,8 @@ func syncArgocdApplication(applicationName, argocdAuthToken string) { } } -func destroyGitlabTerraform(){ +func destroyGitlabTerraform() { + config := configs.ReadConfig() log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") // kubeconfig := os.Getenv("HOME") + "/.kube/config" // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) @@ -499,7 +509,7 @@ func destroyGitlabTerraform(){ os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", home) + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) err := os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) @@ -508,7 +518,7 @@ func destroyGitlabTerraform(){ os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") if !skipGitlabTerraform { - tfInitGitlabCmd := exec.Command(terraformPath, "init") + tfInitGitlabCmd := exec.Command(config.TerraformPath, "init") tfInitGitlabCmd.Stdout = os.Stdout tfInitGitlabCmd.Stderr = os.Stderr err = tfInitGitlabCmd.Run() @@ -516,7 +526,7 @@ func destroyGitlabTerraform(){ log.Panicf("failed to terraform init gitlab %s", err) } - tfDestroyGitlabCmd := exec.Command(terraformPath, "destroy", "-auto-approve") + tfDestroyGitlabCmd := exec.Command(config.TerraformPath, "destroy", "-auto-approve") tfDestroyGitlabCmd.Stdout = os.Stdout tfDestroyGitlabCmd.Stderr = os.Stderr err = tfDestroyGitlabCmd.Run() diff --git a/cmd/stepsBaseInstall.go b/cmd/stepsBaseInstall.go index 5b1ae3d92..cfc92e31f 100644 --- a/cmd/stepsBaseInstall.go +++ b/cmd/stepsBaseInstall.go @@ -2,21 +2,24 @@ package cmd import ( "fmt" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/pkg" + "github.com/spf13/cobra" + "github.com/spf13/viper" "log" "os" "strings" - "github.com/spf13/cobra" - "github.com/spf13/viper" ) -func applyBaseTerraform(cmd *cobra.Command,directory string){ +func applyBaseTerraform(cmd *cobra.Command, directory string) { + config := configs.ReadConfig() applyBase := viper.GetBool("create.terraformapplied.base") if applyBase != true { log.Println("Executing ApplyBaseTerraform") - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, applyBaseTerraform skipped.") return - } + } os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) @@ -25,34 +28,35 @@ func applyBaseTerraform(cmd *cobra.Command,directory string){ if err != nil { log.Panicf("error, directory does not exist - did you `kubefirst init`?: %s \nerror: %v", directory, err) } - _,_,errInit := execShellReturnStrings(terraformPath, "init") + _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") if errInit != nil { panic(fmt.Sprintf("error: terraform init failed %v", err)) } - _,_,errApply := execShellReturnStrings(terraformPath,"apply", "-auto-approve") + _, _, errApply := pkg.ExecShellReturnStrings(config.TerraformPath, "apply", "-auto-approve") if errApply != nil { panic(fmt.Sprintf("error: terraform init failed %v", err)) } - keyOut, _, errKey := execShellReturnStrings(terraformPath, "output", "vault_unseal_kms_key") + keyOut, _, errKey := pkg.ExecShellReturnStrings(config.TerraformPath, "output", "vault_unseal_kms_key") if errKey != nil { log.Panicf("error: terraform apply failed %v", err) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) - keyIdNoSpace := strings.TrimSpace(keyOut) + keyIdNoSpace := strings.TrimSpace(keyOut) keyId := keyIdNoSpace[1 : len(keyIdNoSpace)-1] log.Println("keyid is:", keyId) viper.Set("vault.kmskeyid", keyId) viper.Set("create.terraformapplied.base", true) viper.WriteConfig() - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", home)) + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) } else { log.Println("Skipping: ApplyBaseTerraform") } } -func destroyBaseTerraform(){ +func destroyBaseTerraform() { + config := configs.ReadConfig() if !skipBaseTerraform { - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", home) + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) err := os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) @@ -62,12 +66,12 @@ func destroyBaseTerraform(){ os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - _, _, errInit := execShellReturnStrings(terraformPath, "init") + _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") if errInit != nil { log.Panicf("failed to terraform init base %v", err) } - _, _, errDestroy := execShellReturnStrings(terraformPath, "destroy", "-auto-approve") + _, _, errDestroy := pkg.ExecShellReturnStrings(config.TerraformPath, "destroy", "-auto-approve") if errDestroy != nil { log.Panicf("failed to terraform destroy base %v", err) } diff --git a/cmd/stepsMetaphor.go b/cmd/stepsMetaphor.go index 95c4b705e..dd85f093c 100644 --- a/cmd/stepsMetaphor.go +++ b/cmd/stepsMetaphor.go @@ -2,24 +2,26 @@ package cmd import ( "fmt" - "log" - "github.com/spf13/viper" - "time" - "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5" gitConfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" + "github.com/kubefirst/nebulous/configs" + "github.com/spf13/viper" + "log" + "time" ) func hydrateGitlabMetaphorRepo() { + config := configs.ReadConfig() //TODO: Should this be skipped if already executed? if !viper.GetBool("create.gitlabmetaphor.cloned") { - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") return } - metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", home) + metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", config.HomePath) url := "https://github.com/kubefirst/metaphor-template" @@ -72,4 +74,4 @@ func hydrateGitlabMetaphorRepo() { log.Println("Skipping: hydrateGitlabMetaphorRepo") } -} \ No newline at end of file +} diff --git a/cmd/stepsSoftServe.go b/cmd/stepsSoftServe.go index 18b68766d..04f62de77 100644 --- a/cmd/stepsSoftServe.go +++ b/cmd/stepsSoftServe.go @@ -2,27 +2,30 @@ package cmd import ( "fmt" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/pkg" + "github.com/spf13/viper" + ssh2 "golang.org/x/crypto/ssh" + "io/ioutil" "log" "os" - "strings" - "github.com/spf13/viper" "os/exec" + "strings" "syscall" "time" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" - ssh2 "golang.org/x/crypto/ssh" - "io/ioutil" ) func createSoftServe(kubeconfigPath string) { + config := configs.ReadConfig() if !viper.GetBool("create.softserve.create") { log.Println("Executing createSoftServe") - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, createSoftServe skipped.") return } - toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) + toolsDir := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) err := os.Mkdir(toolsDir, 0777) if err != nil { @@ -30,12 +33,12 @@ func createSoftServe(kubeconfigPath string) { } // create soft-serve stateful set - softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", home) - softServeApplyOut, softServeApplyErr,errSoftServeApply := execShellReturnStrings(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") - log.Printf("Result:\n\t%s\n\t%s\n",softServeApplyOut,softServeApplyErr) + softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", config.HomePath) + softServeApplyOut, softServeApplyErr, errSoftServeApply := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") + log.Printf("Result:\n\t%s\n\t%s\n", softServeApplyOut, softServeApplyErr) if errSoftServeApply != nil { log.Panicf("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) - } + } viper.Set("create.softserve.create", true) viper.WriteConfig() @@ -48,15 +51,16 @@ func createSoftServe(kubeconfigPath string) { } -func configureSoftserveAndPush(){ +func configureSoftserveAndPush() { + config := configs.ReadConfig() configureAndPushFlag := viper.GetBool("create.softserve.configure") if configureAndPushFlag != true { log.Println("Executing configureSoftserveAndPush") - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, configureSoftserveAndPush skipped.") return - } - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") + } + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr err := kPortForward.Start() @@ -77,8 +81,10 @@ func configureSoftserveAndPush(){ } func configureSoftServe() { + config := configs.ReadConfig() + url := "ssh://127.0.0.1:8022/config" - directory := fmt.Sprintf("%s/.kubefirst/config", home) + directory := fmt.Sprintf("%s/.kubefirst/config", config.HomePath) log.Println("git clone", url, directory) @@ -106,7 +112,7 @@ func configureSoftServe() { panic(err) } - println("re-wrote config.yaml", home, "/.kubefirst/config") + println("re-wrote config.yaml", config.HomePath, "/.kubefirst/config") w, _ := repo.Worktree() diff --git a/cmd/stepsVault.go b/cmd/stepsVault.go index a138a9b48..dc10b9870 100644 --- a/cmd/stepsVault.go +++ b/cmd/stepsVault.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" vault "github.com/hashicorp/vault/api" + "github.com/kubefirst/nebulous/configs" internalVault "github.com/kubefirst/nebulous/internal/vault" "github.com/spf13/viper" gitlab "github.com/xanzy/go-gitlab" @@ -15,8 +16,9 @@ import ( ) func configureVault() { + config := configs.ReadConfig() if !viper.GetBool("create.terraformapplied.vault") { - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, configureVault skipped.") return } @@ -33,13 +35,13 @@ func configureVault() { // ``` // ... obviously keep the sensitive values bound to vars - config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + k8sClient, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) if err != nil { - log.Panicf("error: getting config %s", err) + log.Panicf("error: getting k8sClient %s", err) } - clientset, err := kubernetes.NewForConfig(config) + clientset, err := kubernetes.NewForConfig(k8sClient) if err != nil { - log.Panicf("error: getting config &s", err) + log.Panicf("error: getting k8sClient &s", err) } vaultSecretClient = clientset.CoreV1().Secrets("vault") @@ -51,7 +53,7 @@ func configureVault() { viper.Set("vault.token", vaultToken) viper.WriteConfig() - kPortForward := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr err = kPortForward.Start() @@ -75,13 +77,13 @@ func configureVault() { os.Setenv("TF_VAR_vault_token", viper.GetString("aws.hostedzonename")) os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", home) + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", config.HomePath) err = os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) } - tfInitCmd := exec.Command(terraformPath, "init") + tfInitCmd := exec.Command(config.TerraformPath, "init") tfInitCmd.Stdout = os.Stdout tfInitCmd.Stderr = os.Stderr err = tfInitCmd.Run() @@ -89,7 +91,7 @@ func configureVault() { log.Panicf("error: terraform init failed %s", err) } - tfApplyCmd := exec.Command(terraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") + tfApplyCmd := exec.Command(config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") tfApplyCmd.Stdout = os.Stdout tfApplyCmd.Stderr = os.Stderr err = tfApplyCmd.Run() @@ -105,8 +107,9 @@ func configureVault() { } func addGitlabOidcApplications() { + config := configs.ReadConfig() //TODO: Should this skipped if already executed. - if dryrunMode { + if config.DryRun { log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") return } diff --git a/cmd/version.go b/cmd/version.go index fac8a3338..ec6c09117 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -5,8 +5,9 @@ Copyright © 2022 NAME HERE package cmd import ( - "log" + "github.com/kubefirst/nebulous/configs" "github.com/spf13/cobra" + "log" ) func init() { @@ -18,7 +19,8 @@ var versionCmd = &cobra.Command{ Short: "Print the version number for kubefirst-cli", Long: `All software has versions. This is kubefirst's`, Run: func(cmd *cobra.Command, args []string) { - log.Printf("flare-cli golang utility version: v%s", NebolousVersion) - + config := configs.ReadConfig() + log.Printf("flare-cli golang utility version: v%s", config.KubefirstVersion) + }, } diff --git a/cmd/versionConstant.go b/cmd/versionConstant.go deleted file mode 100644 index 5feef682a..000000000 --- a/cmd/versionConstant.go +++ /dev/null @@ -1,5 +0,0 @@ -package cmd - -//This file may be updated/generated by CI/CD to always produce final and unique versions -//allowing easier to track source of bugs. -const NebolousVersion = "0.1.1" \ No newline at end of file diff --git a/configs/config.go b/configs/config.go new file mode 100644 index 000000000..65ce1b2d4 --- /dev/null +++ b/configs/config.go @@ -0,0 +1,64 @@ +package configs + +import ( + "fmt" + "github.com/caarlos0/env/v6" + "log" + "os" + "runtime" +) + +type Config struct { + AwsProfile string `env:"AWS_PROFILE"` + KubefirstLogPath string `env:"KUBEFIRST_LOG_PATH" envDefault:"logs"` + KubectlVersion string `env:"KUBECTL_VERSION" envDefault:"v1.20.0"` + HomePath string + LocalOs string + LocalArchitecture string + KubectlClientPath string + KubeConfigPath string + + TerraformVersion string + TerraformPath string + + HelmClientPath string + HelmVersion string + + DryRun bool + + DestroyBuckets bool + + KubefirstVersion string +} + +func ReadConfig() *Config { + config := Config{} + + if err := env.Parse(&config); err != nil { + log.Println("something went wrong loading the environment variables") + panic(err) + } + + var err error + config.HomePath, err = os.UserHomeDir() + if err != nil { + panic(err) + } + + config.LocalOs = runtime.GOOS + config.LocalArchitecture = runtime.GOARCH + + config.KubectlClientPath = fmt.Sprintf("%s/.kubefirst/tools/kubectl", config.HomePath) + config.KubeConfigPath = fmt.Sprintf("%s/.kubefirst/gitops/terraform/base/kubeconfig_kubefirst", config.HomePath) + config.TerraformPath = fmt.Sprintf("%s/.kubefirst/tools/terraform", config.HomePath) + config.HelmClientPath = fmt.Sprintf("%s/.kubefirst/tools/helm", config.HomePath) + + config.TerraformVersion = "1.0.11" + + // todo adopt latest helmVersion := "v3.9.0" + config.HelmVersion = "v3.2.1" + + config.KubefirstVersion = "0.1.1" + + return &config +} diff --git a/configs/kubefirstDirectory.go b/configs/kubefirstDirectory.go index bdf0146d0..ed747c16e 100755 --- a/configs/kubefirstDirectory.go +++ b/configs/kubefirstDirectory.go @@ -10,7 +10,7 @@ import ( func CheckKubefirstDir(home string) error { k1sDir := fmt.Sprintf("%s/.kubefirst", home) if _, err := os.Stat(k1sDir); err != nil { - errorMsg := fmt.Sprintf("unable to load \".flare\" file, error is: %s", err) + errorMsg := fmt.Sprintf("unable to load \".kubefirst\" directory, error is: %s", err) log.Println(errorMsg) return fmt.Errorf(errorMsg) } diff --git a/go.mod b/go.mod index 057f19c00..3470172ab 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.17 require ( github.com/aws/aws-sdk-go v1.44.23 github.com/aws/aws-sdk-go-v2/config v1.15.7 - github.com/aws/aws-sdk-go-v2/service/eks v1.21.1 github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5 github.com/aws/aws-sdk-go-v2/service/sts v1.16.6 + github.com/caarlos0/env/v6 v6.9.3 github.com/cip8/autoname v1.0.0 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-git/v5 v5.4.2 diff --git a/go.sum b/go.sum index f4c622914..6e5e0ba49 100644 --- a/go.sum +++ b/go.sum @@ -111,8 +111,6 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 h1:eeXdGVtXEe+2Jc49+/v github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6/go.mod h1:FwpAKI+FBPIELJIdmQzlLtRe8LQSOreMcM2wBsPMvvc= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.12 h1:j0VqrjtgsY1Bx27tD0ysay36/K4kFMWRp9K3ieO9nLU= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.12/go.mod h1:00c7+ALdPh4YeEUPXJzyU0Yy01nPGOq2+9rUaz05z9g= -github.com/aws/aws-sdk-go-v2/service/eks v1.21.1 h1:CYRfUZNq2krWxqLE1ntvy40uVT/I6X1PniNHrE/W3ps= -github.com/aws/aws-sdk-go-v2/service/eks v1.21.1/go.mod h1:kLodo8S0UEoVEd3mHTKtGnAhCT2uvCUM/Jjfr3og1yI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.5 h1:gRW1ZisKc93EWEORNJRvy/ZydF3o6xLSveJHdi1Oa0U= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.5/go.mod h1:ZbkttHXaVn3bBo/wpJbQGiiIWR90eTBUVBrEHUEQlho= github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5 h1:Vex6D07/CmahT0LIaiRk+j9xyVAsvQxBa8iv38p3Ajc= @@ -128,6 +126,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/caarlos0/env/v6 v6.9.3 h1:Tyg69hoVXDnpO5Qvpsu8EoquarbPyQb+YwExWHP8wWU= +github.com/caarlos0/env/v6 v6.9.3/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/cmd/aws.go b/internal/aws/aws.go similarity index 89% rename from cmd/aws.go rename to internal/aws/aws.go index 5ed216924..0e8433892 100644 --- a/cmd/aws.go +++ b/internal/aws/aws.go @@ -1,28 +1,29 @@ -package cmd +package aws import ( + "context" + "fmt" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/route53" "github.com/aws/aws-sdk-go-v2/service/route53/types" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/cip8/autoname" + "github.com/kubefirst/nebulous/configs" "github.com/spf13/viper" "log" + "net" "os" - "strings" - "fmt" - "context" "strconv" - "net" + "strings" "time" ) - -func bucketRand() { +func BucketRand() { + cfg := configs.ReadConfig() sess, err := session.NewSession(&aws.Config{ Region: aws.String(viper.GetString("aws.region"))}, ) @@ -46,7 +47,7 @@ func bucketRand() { regionName := viper.GetString("aws.region") log.Println("region is ", regionName) - if !dryrunMode { + if !cfg.DryRun { if regionName == "us-east-1" { _, err = s3Client.CreateBucket(&s3.CreateBucketInput{ Bucket: &bucketName, @@ -71,11 +72,11 @@ func bucketRand() { viper.WriteConfig() } log.Printf("bucket %s exists", viper.GetString(fmt.Sprintf("bucket.%s.name", bucket))) - Trackers[trackerStage7].Tracker.Increment(int64(1)) + //Trackers[trackerStage7].Tracker.Increment(int64(1)) } } -func getAccountInfo() { +func GetAccountInfo() { cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Panicf("failed to load configuration, error: %s", err) @@ -91,7 +92,7 @@ func getAccountInfo() { viper.WriteConfig() } -func testHostedZoneLiveness(hostedZoneName, hostedZoneId string) { +func TestHostedZoneLiveness(hostedZoneName, hostedZoneId string) { //tracker := progress.Tracker{Message: "testing hosted zone", Total: 25} // todo need to create single client and pass it @@ -121,7 +122,8 @@ func testHostedZoneLiveness(hostedZoneName, hostedZoneId string) { } if len(recordList.ResourceRecordSets) == 0 { - if !dryrunMode { + cfg := configs.ReadConfig() + if !cfg.DryRun { record, err := route53Client.ChangeResourceRecordSets(context.TODO(), &route53.ChangeResourceRecordSetsInput{ ChangeBatch: &types.ChangeBatch{ Changes: []types.Change{ @@ -215,7 +217,7 @@ func testHostedZoneLiveness(hostedZoneName, hostedZoneId string) { } -func getDNSInfo(hostedZoneName string) string { +func GetDNSInfo(hostedZoneName string) string { cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { @@ -234,7 +236,7 @@ func getDNSInfo(hostedZoneName string) string { for _, zone := range hostedZones.HostedZones { if *zone.Name == fmt.Sprintf(`%s%s`, hostedZoneName, ".") { - zoneId = returnHostedZoneId(*zone.Id) + zoneId = ReturnHostedZoneId(*zone.Id) log.Printf(`found entry for user submitted domain %s, using hosted zone id %s`, hostedZoneName, zoneId) viper.Set("aws.hostedzonename", hostedZoneName) viper.Set("aws.domainid", zoneId) @@ -245,12 +247,11 @@ func getDNSInfo(hostedZoneName string) string { } -func returnHostedZoneId(rawZoneId string) string { +func ReturnHostedZoneId(rawZoneId string) string { return strings.Split(rawZoneId, "/")[2] } - -func listBucketsInUse() []string{ +func ListBucketsInUse() []string { //Read flare file //Iterate over buckets //check if bucket exist @@ -259,30 +260,30 @@ func listBucketsInUse() []string{ var bucketsInUse []string bucketsConfig := viper.AllKeys() for _, bucketKey := range bucketsConfig { - match := strings.HasPrefix(bucketKey,"bucket.") && strings.HasSuffix(bucketKey,".name") + match := strings.HasPrefix(bucketKey, "bucket.") && strings.HasSuffix(bucketKey, ".name") if match { bucketName := viper.GetString(bucketKey) - bucketsInUse = append(bucketsInUse,bucketName) - } + bucketsInUse = append(bucketsInUse, bucketName) + } } return bucketsInUse } -func destroyBucket(bucketName string) { - - s3Client := s3.New(getAWSSession()) +func DestroyBucket(bucketName string) { + + s3Client := s3.New(GetAWSSession()) log.Printf("Attempt to delete: %s", bucketName) _, errHead := s3Client.HeadBucket(&s3.HeadBucketInput{ Bucket: &bucketName, - }) + }) if errHead != nil { if aerr, ok := errHead.(awserr.Error); ok { switch aerr.Code() { case s3.ErrCodeNoSuchBucket: log.Println("Bucket Error:", s3.ErrCodeNoSuchBucket, aerr.Error()) default: - log.Println("Bucket Error:",aerr.Error()) + log.Println("Bucket Error:", aerr.Error()) } } else { // Print the error, cast err to awserr.Error to get the Code and @@ -302,7 +303,7 @@ func destroyBucket(bucketName string) { } -func getAWSSession() *session.Session { +func GetAWSSession() *session.Session { sess, err := session.NewSession(&aws.Config{ Region: aws.String(viper.GetString("aws.region"))}, ) @@ -312,13 +313,14 @@ func getAWSSession() *session.Session { return sess } -func destroyBucketsInUse(){ - if destroyBuckets { - log.Println("Execute: destroyBucketsInUse") - for _,bucket := range listBucketsInUse() { - destroyBucket(bucket) +func DestroyBucketsInUse() { + cfg := configs.ReadConfig() + if cfg.DestroyBuckets { + log.Println("Execute: DestroyBucketsInUse") + for _, bucket := range ListBucketsInUse() { + DestroyBucket(bucket) } } else { - log.Println("Skip: destroyBucketsInUse") + log.Println("Skip: DestroyBucketsInUse") } -} \ No newline at end of file +} diff --git a/cmd/files.go b/internal/downloadManager/download.go similarity index 52% rename from cmd/files.go rename to internal/downloadManager/download.go index 50a1e4621..33bfef91a 100644 --- a/cmd/files.go +++ b/internal/downloadManager/download.go @@ -1,93 +1,145 @@ -package cmd +package downloadManager import ( - "io" - "fmt" - "log" - "os" "archive/tar" "archive/zip" "compress/gzip" + "fmt" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/pkg" + "io" + "log" "net/http" + "os" "path/filepath" "strings" ) -func download() { - toolsDir := fmt.Sprintf("%s/.kubefirst/tools", home) +func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracker) error { + + toolsDir := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) err := os.Mkdir(toolsDir, 0777) if err != nil { - log.Println("error creating directory %s", toolsDir, err) + log.Printf("error creating directory %s, error is: %s\n", toolsDir, err) + } + + kubectlVersion := config.KubectlVersion + kubectlDownloadUrl := fmt.Sprintf( + "https://dl.k8s.io/release/%s/bin/%s/%s/kubectl", + kubectlVersion, + config.LocalOs, + config.LocalArchitecture, + ) + + err = downloadFile(config.KubectlClientPath, kubectlDownloadUrl) + if err != nil { + return err + } + + err = os.Chmod(config.KubectlClientPath, 0755) + if err != nil { + return err } - kubectlVersion := "v1.20.0" - kubectlDownloadUrl := fmt.Sprintf("https://dl.k8s.io/release/%s/bin/%s/%s/kubectl", kubectlVersion, localOs, localArchitecture) - downloadFile(kubectlClientPath, kubectlDownloadUrl) - os.Chmod(kubectlClientPath, 0755) + // todo: this kubeconfig is not available to us until we have run the terraform in base/ + err = os.Setenv("KUBECONFIG", config.KubeConfigPath) + if err != nil { + return err + } - // todo this kubeconfig is not available to us until we have run the terraform in base/ - os.Setenv("KUBECONFIG", kubeconfigPath) log.Println("going to print the kubeconfig env in runtime", os.Getenv("KUBECONFIG")) - kubectlStdOut, kubectlStdErr,errKubectl := execShellReturnStrings(kubectlClientPath, "version", "--client", "--short") - log.Printf("-> kubectl version:\n\t%s\n\t%s\n",kubectlStdOut,kubectlStdErr) + kubectlStdOut, kubectlStdErr, errKubectl := pkg.ExecShellReturnStrings(config.KubectlClientPath, "version", "--client", "--short") + log.Printf("-> kubectl version:\n\t%s\n\t%s\n", kubectlStdOut, kubectlStdErr) if errKubectl != nil { log.Panicf("failed to call kubectlVersionCmd.Run(): %v", err) } - Trackers[trackerStage5].Tracker.Increment(int64(1)) - // argocdVersion := "v2.3.4" - // argocdDownloadUrl := fmt.Sprintf("https://github.com/argoproj/argo-cd/releases/download/%s/argocd-%s-%s", argocdVersion, localOs, localArchitecture) - // argocdClientPath := fmt.Sprintf("%s/.kubefirst/tools/argocd", home) - // downloadFile(argocdClientPath, argocdDownloadUrl) - // os.Chmod(argocdClientPath, 755) - - // argocdVersionCmd := exec.Command(argocdClientPath, "version", "--client", "--short") - // argocdVersionCmd.Stdout = os.Stdout - // argocdVersionCmd.Stderr = os.Stderr - // err = argocdVersionCmd.Run() - // if err != nil { - // fmt.Println("failed to call argocdVersionCmd.Run(): %v", err) - // } - - // todo adopt latest helmVersion := "v3.9.0" - terraformVersion := "1.0.11" - // terraformClientPath := fmt.Sprintf("./%s-%s/terraform", localOs, localArchitecture) - terraformDownloadUrl := fmt.Sprintf("https://releases.hashicorp.com/terraform/%s/terraform_%s_%s_%s.zip", terraformVersion, terraformVersion, localOs, localArchitecture) - terraformDownloadZipPath := fmt.Sprintf("%s/.kubefirst/tools/terraform.zip", home) - downloadFile(terraformDownloadZipPath, terraformDownloadUrl) - // terraformZipDownload, err := os.Open(terraformDownloadZipPath) - if err != nil { - log.Panicf("error reading terraform file") - } - unzipDirectory := fmt.Sprintf("%s/.kubefirst/tools", home) + const trackerStage20 = "0 - Apply Base" + const trackerStage5 = "6 - DownloadTools Tools" + + trackers[trackerStage20] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage20, 1)} + trackers[trackerStage5].Tracker.Increment(1) + + // todo: adopt latest helmVersion := "v3.9.0" + terraformVersion := config.TerraformVersion + + terraformDownloadUrl := fmt.Sprintf( + "https://releases.hashicorp.com/terraform/%s/terraform_%s_%s_%s.zip", + terraformVersion, + terraformVersion, + config.LocalOs, + config.LocalArchitecture, + ) + + terraformDownloadZipPath := fmt.Sprintf("%s/.kubefirst/tools/terraform.zip", config.HomePath) + err = downloadFile(terraformDownloadZipPath, terraformDownloadUrl) + if err != nil { + log.Println("error reading terraform file") + return err + } + + unzipDirectory := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) unzip(terraformDownloadZipPath, unzipDirectory) - os.Chmod(unzipDirectory, 0777) - os.Chmod(fmt.Sprintf("%s/terraform", unzipDirectory), 0755) - Trackers[trackerStage5].Tracker.Increment(int64(1)) + err = os.Chmod(unzipDirectory, 0777) + if err != nil { + return err + } + + err = os.Chmod(fmt.Sprintf("%s/terraform", unzipDirectory), 0755) + if err != nil { + return err + } + + trackers[trackerStage5].Tracker.Increment(int64(1)) + + helmVersion := config.HelmVersion + helmDownloadUrl := fmt.Sprintf( + "https://get.helm.sh/helm-%s-%s-%s.tar.gz", + helmVersion, + config.LocalOs, + config.LocalArchitecture, + ) + + helmDownloadTarGzPath := fmt.Sprintf("%s/.kubefirst/tools/helm.tar.gz", config.HomePath) + err = downloadFile(helmDownloadTarGzPath, helmDownloadUrl) + if err != nil { + return err + } - // todo adopt latest helmVersion := "v3.9.0" - helmVersion := "v3.2.1" - helmDownloadUrl := fmt.Sprintf("https://get.helm.sh/helm-%s-%s-%s.tar.gz", helmVersion, localOs, localArchitecture) - helmDownloadTarGzPath := fmt.Sprintf("%s/.kubefirst/tools/helm.tar.gz", home) - downloadFile(helmDownloadTarGzPath, helmDownloadUrl) helmTarDownload, err := os.Open(helmDownloadTarGzPath) if err != nil { log.Panicf("could not read helm download content") } - extractFileFromTarGz(helmTarDownload, fmt.Sprintf("%s-%s/helm", localOs, localArchitecture), helmClientPath) - os.Chmod(helmClientPath, 0755) - helmStdOut, helmStdErr,errHelm := execShellReturnStrings(helmClientPath, "version", "--client", "--short") - log.Printf("-> kubectl version:\n\t%s\n\t%s\n",helmStdOut,helmStdErr) + + extractFileFromTarGz( + helmTarDownload, + fmt.Sprintf("%s-%s/helm", config.LocalOs, config.LocalArchitecture), + config.HelmClientPath, + ) + err = os.Chmod(config.HelmClientPath, 0755) + if err != nil { + return err + } + + helmStdOut, helmStdErr, errHelm := pkg.ExecShellReturnStrings( + config.HelmClientPath, + "version", + "--client", + "--short", + ) + + log.Printf("-> kubectl version:\n\t%s\n\t%s\n", helmStdOut, helmStdErr) // currently argocd init values is generated by flare nebulous ssh // todo helm install argocd --create-namespace --wait --values ~/.kubefirst/argocd-init-values.yaml argo/argo-cd if errHelm != nil { log.Panicf("error executing helm version command: %v", err) } - Trackers[trackerStage5].Tracker.Increment(int64(1)) + trackers[trackerStage5].Tracker.Increment(int64(1)) + return nil } func downloadFile(filepath string, url string) (err error) { @@ -177,27 +229,27 @@ func extractTarGz(gzipStream io.Reader) { } p, _ := filepath.Abs(header.Name) if !strings.Contains(p, "..") { - + switch header.Typeflag { - case tar.TypeDir: - if err := os.Mkdir(header.Name, 0755); err != nil { - log.Println("extractTarGz: Mkdir() failed: %s", err.Error()) - } - case tar.TypeReg: - outFile, err := os.Create(header.Name) - if err != nil { - log.Println("extractTarGz: Create() failed: %s", err.Error()) - } - if _, err := io.Copy(outFile, tarReader); err != nil { - log.Println("extractTarGz: Copy() failed: %s", err.Error()) - } - outFile.Close() - - default: - log.Println( - "extractTarGz: uknown type: %s in %s", - header.Typeflag, - header.Name) + case tar.TypeDir: + if err := os.Mkdir(header.Name, 0755); err != nil { + log.Println("extractTarGz: Mkdir() failed: %s", err.Error()) + } + case tar.TypeReg: + outFile, err := os.Create(header.Name) + if err != nil { + log.Println("extractTarGz: Create() failed: %s", err.Error()) + } + if _, err := io.Copy(outFile, tarReader); err != nil { + log.Println("extractTarGz: Copy() failed: %s", err.Error()) + } + outFile.Close() + + default: + log.Println( + "extractTarGz: uknown type: %s in %s", + header.Typeflag, + header.Name) } } @@ -247,4 +299,4 @@ func unzip(zipFilepath string, unzipDirectory string) { dstFile.Close() fileInArchive.Close() } -} \ No newline at end of file +} diff --git a/main.go b/main.go index 188f6831a..786bf86ab 100644 --- a/main.go +++ b/main.go @@ -5,26 +5,26 @@ Copyright © 2022 Kubefirst Inc. devops@kubefirst.com package main import ( + "fmt" "github.com/kubefirst/nebulous/cmd" "log" "os" "time" - "fmt" ) func main() { now := time.Now() epoch := now.Unix() logsdir := "log" - os.Mkdir(logsdir,0700) - logfile := fmt.Sprintf("./%s/log_%d.log",logsdir,epoch) - fmt.Printf("Result will be logged at: %s \n",logfile) + os.Mkdir(logsdir, 0700) + logfile := fmt.Sprintf("./%s/log_%d.log", logsdir, epoch) + fmt.Printf("Result will be logged at: %s \n", logfile) file, err := openLogFile(logfile) defer file.Close() log.SetOutput(file) - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } log.SetPrefix("LOG: ") log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) @@ -32,9 +32,9 @@ func main() { } func openLogFile(path string) (*os.File, error) { - logFile, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) - if err != nil { - return nil, err - } - return logFile, nil -} \ No newline at end of file + logFile, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) + if err != nil { + return nil, err + } + return logFile, nil +} diff --git a/pkg/flare/progressTracker.go b/pkg/progress_bar.go similarity index 62% rename from pkg/flare/progressTracker.go rename to pkg/progress_bar.go index f1de4e687..03f088828 100644 --- a/pkg/flare/progressTracker.go +++ b/pkg/progress_bar.go @@ -1,10 +1,9 @@ -package flare +package pkg import ( "flag" "fmt" "github.com/jedib0t/go-pretty/v6/progress" - "github.com/jedib0t/go-pretty/v6/text" "time" ) @@ -12,7 +11,20 @@ type ActionTracker struct { Tracker *progress.Tracker } +const TrackerStage0 = "1 - Load properties" +const TrackerStage1 = "2 - Set .flare initial values" +const TrackerStage2 = "3 - Test Domain Liveness" +const TrackerStage3 = "4 - Create SSH Key Pair" +const TrackerStage4 = "5 - Load Templates" +const TrackerStage5 = "6 - DownloadTools Tools" +const TrackerStage6 = "7 - Get Account Info" +const TrackerStage7 = "8 - Create Buckets" +const TrackerStage8 = "9 - Detokenize" +const TrackerStage9 = "10 - Send Telemetry" + var ( + pw progress.Writer + Trackers map[string]*ActionTracker flagAutoStop = flag.Bool("auto-stop", false, "Auto-stop rendering?") flagHideETA = flag.Bool("hide-eta", false, "Hide the ETA?") flagHideETAOverall = flag.Bool("hide-eta-overall", false, "Hide the ETA in the overall tracker?") @@ -20,23 +32,34 @@ var ( flagHidePercentage = flag.Bool("hide-percentage", false, "Hide the progress percent?") flagHideTime = flag.Bool("hide-time", false, "Hide the time taken?") flagHideValue = flag.Bool("hide-value", false, "Hide the tracker value?") - // flagNumTrackers = flag.Int("num-trackers", 12, "Number of Trackers") - flagRandomFail = flag.Bool("rnd-fail", false, "Introduce random failures in tracking") - flagRandomLogs = flag.Bool("rnd-logs", false, "Output random logs in the middle of tracking") +) - messageColors = []text.Color{ - text.FgRed, - text.FgGreen, - text.FgYellow, - text.FgBlue, - text.FgMagenta, - text.FgCyan, - text.FgWhite, +// GetTrackers keeps one single instance of Trackers alive using singleton pattern. +func GetTrackers() map[string]*ActionTracker { + if Trackers != nil { + return Trackers + } + Trackers = make(map[string]*ActionTracker) + return Trackers +} + +// CreateTracker receives Tracker data and return a new Tracker. +func CreateTracker(title string, total int64) *progress.Tracker { + + tracker := &progress.Tracker{ + Message: title, + Total: total, + Units: progress.UnitsDefault, } -) -var pw progress.Writer + pw.AppendTracker(tracker) + + return tracker +} + +// SetupProgress prepare the progress bar setting its initial configuration func SetupProgress(numTrackers int) { + flagNumTrackers := flag.Int("num-trackers", numTrackers, "Number of Trackers") flag.Parse() fmt.Printf("Init actions: %d expected tasks ...\n\n", *flagNumTrackers) @@ -59,13 +82,4 @@ func SetupProgress(numTrackers int) { pw.Style().Visibility.TrackerOverall = !*flagHideOverallTracker pw.Style().Visibility.Value = !*flagHideValue go pw.Render() - -} - -func CreateTracker(title string, total int64) *progress.Tracker { - units := &progress.UnitsDefault - message := title - tracker := progress.Tracker{Message: message, Total: total, Units: *units} - pw.AppendTracker(&tracker) - return &tracker } diff --git a/cmd/shell.go b/pkg/shell.go similarity index 80% rename from cmd/shell.go rename to pkg/shell.go index 54b29c8c7..f44337ea6 100644 --- a/cmd/shell.go +++ b/pkg/shell.go @@ -1,13 +1,13 @@ -package cmd +package pkg import ( - "log" "bytes" + "log" "os/exec" ) -func execShellReturnStrings(command string, args ...string) (string, string, error) { - var outb, errb bytes.Buffer +func ExecShellReturnStrings(command string, args ...string) (string, string, error) { + var outb, errb bytes.Buffer k := exec.Command(command, args...) k.Stdout = &outb k.Stderr = &errb From 4928b34f1494017e09e24628bba6b9a7832ee0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 16:53:22 -0300 Subject: [PATCH 042/107] refactor: move kubernetes client requests to internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/checktools.go | 4 -- cmd/clean.go | 4 -- cmd/destroy.go | 46 ++++--------------- cmd/init.go | 4 -- cmd/kubernetes.go | 63 ------------------------- cmd/stepsArgo.go | 24 +++++----- cmd/stepsVault.go | 5 +- configs/config.go | 6 +-- internal/k8s/kubernetes.go | 94 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 121 insertions(+), 129 deletions(-) delete mode 100644 cmd/kubernetes.go create mode 100644 internal/k8s/kubernetes.go diff --git a/cmd/checktools.go b/cmd/checktools.go index 7731990f5..f962fdaf0 100644 --- a/cmd/checktools.go +++ b/cmd/checktools.go @@ -1,7 +1,3 @@ -/* -Copyright © 2022 NAME HERE - -*/ package cmd import ( diff --git a/cmd/clean.go b/cmd/clean.go index fe5305aeb..4faf749a0 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -1,7 +1,3 @@ -/* -Copyright © 2022 NAME HERE - -*/ package cmd import ( diff --git a/cmd/destroy.go b/cmd/destroy.go index b1c2f0c1a..ed6a1a1cc 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -1,15 +1,10 @@ -/* -Copyright © 2022 NAME HERE - -*/ package cmd import ( - "fmt" "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/internal/aws" + "github.com/kubefirst/nebulous/internal/k8s" "github.com/spf13/cobra" - "github.com/spf13/viper" "log" "os" "os/exec" @@ -17,7 +12,8 @@ import ( ) var skipGitlabTerraform bool -var skipDeleteRegistryApplication bool + +//var skipDeleteRegistryApplication bool var skipBaseTerraform bool var DestroyBuckets bool @@ -26,7 +22,7 @@ var destroyCmd = &cobra.Command{ Use: "destroy", Short: "destroy the kubefirst management cluster", Long: `destory the kubefirst management cluster -and all of the components in kubernetes. +and all of the components in k8s. Optional: skip gitlab terraform if the registry has already been delteted.`, @@ -45,46 +41,20 @@ if the registry has already been delteted.`, // todo this needs to be removed when we are no longer in the starter account destroyGitlabTerraform() // delete argocd registry - deleteRegistryApplication() + k8s.DeleteRegistryApplication() destroyBaseTerraform() //TODO: Remove buckets? Opt-in flag aws.DestroyBucketsInUse() - }, } func init() { + config := configs.ReadConfig() + rootCmd.AddCommand(destroyCmd) destroyCmd.PersistentFlags().BoolVar(&skipGitlabTerraform, "skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") - destroyCmd.PersistentFlags().BoolVar(&skipDeleteRegistryApplication, "skip-delete-register", false, "whether to skip deletion of resgister application ") + destroyCmd.PersistentFlags().BoolVar(&config.SkipDeleteRegistryApplication, "skip-delete-register", false, "whether to skip deletion of resgister application ") destroyCmd.PersistentFlags().BoolVar(&skipBaseTerraform, "skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") destroyCmd.PersistentFlags().BoolVar(&DestroyBuckets, "destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") } - -func deleteRegistryApplication() { - config := configs.ReadConfig() - if !skipDeleteRegistryApplication { - log.Println("starting port forward to argocd server and deleting registry") - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to argocd %s", err) - } - - url := "https://localhost:8080/api/v1/applications/registry" - argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) - argoCdAppSync.Stdout = os.Stdout - argoCdAppSync.Stderr = os.Stderr - err = argoCdAppSync.Run() - if err != nil { - log.Panicf("error: curl appSync failed failed %s", err) - } - log.Println("deleting argocd application registry") - } else { - log.Println("skip: deleteRegistryApplication") - } -} diff --git a/cmd/init.go b/cmd/init.go index b25ff87a1..47b968ba9 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -55,10 +55,6 @@ to quickly create a Cobra application.`, metricName := "kubefirst.init.started" metricDomain := hostedZoneName - log.Println("---debug---") - log.Println(config.DryRun) - log.Println("---debug---") - if !config.DryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go deleted file mode 100644 index 1ee412758..000000000 --- a/cmd/kubernetes.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright © 2022 NAME HERE - -*/ -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "log" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" -) - -var vaultRootToken string -var gitlabToolboxPodName string - -// API client for managing secrets & pods -var gitlabSecretClient coreV1Types.SecretInterface -var vaultSecretClient coreV1Types.SecretInterface -var argocdSecretClient coreV1Types.SecretInterface -var gitlabPodsClient coreV1Types.PodInterface - - -func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { - pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) - if err != nil { - fmt.Println(err) - } - - gitlabToolboxPodName = pods.Items[0].Name - - return gitlabToolboxPodName -} - -func getVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { - name := "vault-unseal-keys" - log.Printf("Reading secret %s\n", name) - secret, err := vaultSecretClient.Get(context.TODO(), name, metaV1.GetOptions{}) - - if err != nil { - panic(err.Error()) - } - - var jsonData map[string]interface{} - - for _, value := range secret.Data { - if err := json.Unmarshal(value, &jsonData); err != nil { - panic(err) - } - vaultRootToken = jsonData["root_token"].(string) - } - return vaultRootToken -} - -func getSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key string) string { - secret, err := k8sClient.Get(context.TODO(), secretName, metaV1.GetOptions{}) - if err != nil { - log.Println(fmt.Sprintf("error getting key: %s from secret: %s", key, secretName), err) - } - return string(secret.Data[key]) -} diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index 58b0b175f..dffa08631 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -13,6 +13,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/k8s" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" "html/template" @@ -111,20 +112,21 @@ func produceGitlabTokens() { return } time.Sleep(30 * time.Second) - argocdSecretClient = clientset.CoreV1().Secrets("argocd") + // todo: move it to config + k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") - argocdPassword := getSecretValue(argocdSecretClient, "argocd-initial-admin-secret", "password") + argocdPassword := k8s.GetSecretValue(k8s.ArgocdSecretClient, "argocd-initial-admin-secret", "password") viper.Set("argocd.admin.password", argocdPassword) viper.WriteConfig() log.Println("discovering gitlab toolbox pod") - gitlabPodsClient = clientset.CoreV1().Pods("gitlab") - gitlabPodName := getPodNameByLabel(gitlabPodsClient, "toolbox") + k8s.GitlabPodsClient = clientset.CoreV1().Pods("gitlab") + gitlabPodName := k8s.GetPodNameByLabel(k8s.GitlabPodsClient, "toolbox") - gitlabSecretClient = clientset.CoreV1().Secrets("gitlab") - secrets, err := gitlabSecretClient.List(context.TODO(), metaV1.ListOptions{}) + k8s.GitlabSecretClient = clientset.CoreV1().Secrets("gitlab") + secrets, err := k8s.GitlabSecretClient.List(context.TODO(), metaV1.ListOptions{}) var gitlabRootPasswordSecretName string @@ -134,7 +136,7 @@ func produceGitlabTokens() { log.Println("gitlab initial root password secret name: ", gitlabRootPasswordSecretName) } } - gitlabRootPassword := getSecretValue(gitlabSecretClient, gitlabRootPasswordSecretName, "password") + gitlabRootPassword := k8s.GetSecretValue(k8s.GitlabSecretClient, gitlabRootPasswordSecretName, "password") viper.Set("gitlab.podname", gitlabPodName) viper.Set("gitlab.root.password", gitlabRootPassword) @@ -154,7 +156,7 @@ func produceGitlabTokens() { if gitlabRunnerToken == "" { log.Println("getting gitlab runner token") - gitlabRunnerRegistrationToken := getSecretValue(gitlabSecretClient, "gitlab-gitlab-runner-secret", "runner-registration-token") + gitlabRunnerRegistrationToken := k8s.GetSecretValue(k8s.GitlabSecretClient, "gitlab-gitlab-runner-secret", "runner-registration-token") viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) viper.WriteConfig() } @@ -326,7 +328,7 @@ func changeRegistryToGitLab() { if err != nil { log.Panicf("error getting kubeconfig for clientset") } - argocdSecretClient = clientset.CoreV1().Secrets("argocd") + k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") var secrets bytes.Buffer @@ -353,7 +355,7 @@ func changeRegistryToGitLab() { ba := []byte(secrets.String()) err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) if err != nil { log.Panicf("error creating argocd repository credentials template secret %s", err) } @@ -383,7 +385,7 @@ func changeRegistryToGitLab() { ba = []byte(repoSecrets.String()) err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - _, err = argocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) if err != nil { log.Panicf("error creating argocd repository connection secret %s", err) } diff --git a/cmd/stepsVault.go b/cmd/stepsVault.go index dc10b9870..67c02f91b 100644 --- a/cmd/stepsVault.go +++ b/cmd/stepsVault.go @@ -4,6 +4,7 @@ import ( "fmt" vault "github.com/hashicorp/vault/api" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/k8s" internalVault "github.com/kubefirst/nebulous/internal/vault" "github.com/spf13/viper" gitlab "github.com/xanzy/go-gitlab" @@ -44,8 +45,8 @@ func configureVault() { log.Panicf("error: getting k8sClient &s", err) } - vaultSecretClient = clientset.CoreV1().Secrets("vault") - vaultToken, err := internalVault.GetVaultRootToken(vaultSecretClient) + k8s.VaultSecretClient = clientset.CoreV1().Secrets("vault") + vaultToken, err := internalVault.GetVaultRootToken(k8s.VaultSecretClient) if err != nil { log.Panicf("unable to get vault root token, error: %s", err) } diff --git a/configs/config.go b/configs/config.go index 65ce1b2d4..470b44f94 100644 --- a/configs/config.go +++ b/configs/config.go @@ -24,9 +24,9 @@ type Config struct { HelmClientPath string HelmVersion string - DryRun bool - - DestroyBuckets bool + DryRun bool + SkipDeleteRegistryApplication bool + DestroyBuckets bool KubefirstVersion string } diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go new file mode 100644 index 000000000..fcfad866b --- /dev/null +++ b/internal/k8s/kubernetes.go @@ -0,0 +1,94 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package k8s + +import ( + "context" + "encoding/json" + "fmt" + "github.com/kubefirst/nebulous/configs" + "github.com/spf13/viper" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" + "log" + "os" + "os/exec" + "syscall" +) + +var vaultRootToken string +var gitlabToolboxPodName string + +// API client for managing secrets & pods +var GitlabSecretClient coreV1Types.SecretInterface +var VaultSecretClient coreV1Types.SecretInterface +var ArgocdSecretClient coreV1Types.SecretInterface +var GitlabPodsClient coreV1Types.PodInterface + +func GetPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { + pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) + if err != nil { + fmt.Println(err) + } + + gitlabToolboxPodName = pods.Items[0].Name + + return gitlabToolboxPodName +} + +func getVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { + name := "vault-unseal-keys" + log.Printf("Reading secret %s\n", name) + secret, err := vaultSecretClient.Get(context.TODO(), name, metaV1.GetOptions{}) + + if err != nil { + panic(err.Error()) + } + + var jsonData map[string]interface{} + + for _, value := range secret.Data { + if err := json.Unmarshal(value, &jsonData); err != nil { + panic(err) + } + vaultRootToken = jsonData["root_token"].(string) + } + return vaultRootToken +} + +func GetSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key string) string { + secret, err := k8sClient.Get(context.TODO(), secretName, metaV1.GetOptions{}) + if err != nil { + log.Println(fmt.Sprintf("error getting key: %s from secret: %s", key, secretName), err) + } + return string(secret.Data[key]) +} + +func DeleteRegistryApplication() { + config := configs.ReadConfig() + if !config.SkipDeleteRegistryApplication { + log.Println("starting port forward to argocd server and deleting registry") + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd %s", err) + } + + url := "https://localhost:8080/api/v1/applications/registry" + argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) + argoCdAppSync.Stdout = os.Stdout + argoCdAppSync.Stderr = os.Stderr + err = argoCdAppSync.Run() + if err != nil { + log.Panicf("error: curl appSync failed failed %s", err) + } + log.Println("deleting argocd application registry") + } else { + log.Println("skip: deleteRegistryApplication") + } +} From 78e9b268efef6ee0145d6cca763e3f69255feec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 16:56:35 -0300 Subject: [PATCH 043/107] refactor: move kubernetes client requests to internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/gitlab.go | 68 --------------------------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 cmd/gitlab.go diff --git a/cmd/gitlab.go b/cmd/gitlab.go deleted file mode 100644 index b0ed0fd8d..000000000 --- a/cmd/gitlab.go +++ /dev/null @@ -1,68 +0,0 @@ -package cmd - -import ( - "crypto/tls" - "encoding/json" - "fmt" - "github.com/kubefirst/nebulous/configs" - "log" - "net/http" - "net/url" - "os" - "os/exec" - "syscall" - - "github.com/google/uuid" - "github.com/spf13/viper" -) - -func gitlabGeneratePersonalAccessToken(gitlabPodName string) { - config := configs.ReadConfig() - - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to gitlab %s", err) - } - - log.Println("generating gitlab personal access token on pod: ", gitlabPodName) - - id := uuid.New() - gitlabToken := id.String()[:20] - - k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err = k.Run() - if err != nil { - log.Panicf("error running exec against %s to generate gitlab personal access token for root user", gitlabPodName) - } - - viper.Set("gitlab.token", gitlabToken) - viper.WriteConfig() - - log.Println("gitlab personal access token generated", gitlabToken) -} - -func uploadGitlabSSHKey(gitlabToken string) { - data := url.Values{ - "title": {"kubefirst"}, - "key": {viper.GetString("botpublickey")}, - } - - gitlabUrlBase := fmt.Sprintf("http://localhost:8888") - - http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) - if err != nil { - log.Panicf("error: failed to upload ssh key to gitlab") - } - var res map[string]interface{} - json.NewDecoder(resp.Body).Decode(&res) - log.Println("ssh public key uploaded to gitlab") - viper.Set("gitlab.keyuploaded", true) - viper.WriteConfig() -} From 17a77fca35abf4ebc39656351dc6243e2dfc7459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 16:56:56 -0300 Subject: [PATCH 044/107] refactor: move gitlab functions to gitlab internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/stepsArgo.go | 3 ++- internal/gitlab/gitlab.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index dffa08631..2fa342b04 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -13,6 +13,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/k8s" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" @@ -147,7 +148,7 @@ func produceGitlabTokens() { if gitlabToken == "" { log.Println("generating gitlab personal access token") - gitlabGeneratePersonalAccessToken(gitlabPodName) + gitlab.GitlabGeneratePersonalAccessToken(gitlabPodName) } diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index c1a007a04..dd69e94b6 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -5,6 +5,14 @@ import ( "crypto/rsa" "crypto/x509" "encoding/pem" + "fmt" + "github.com/google/uuid" + "github.com/kubefirst/nebulous/configs" + "github.com/spf13/viper" + "log" + "os" + "os/exec" + "syscall" "golang.org/x/crypto/ssh" ) @@ -33,3 +41,34 @@ func GenerateKey() (string, string, error) { return publicKey, privateKey, nil } + +func GitlabGeneratePersonalAccessToken(gitlabPodName string) { + config := configs.ReadConfig() + + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to gitlab %s", err) + } + + log.Println("generating gitlab personal access token on pod: ", gitlabPodName) + + id := uuid.New() + gitlabToken := id.String()[:20] + + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + log.Panicf("error running exec against %s to generate gitlab personal access token for root user", gitlabPodName) + } + + viper.Set("gitlab.token", gitlabToken) + viper.WriteConfig() + + log.Println("gitlab personal access token generated", gitlabToken) +} From 07d56c11ab8e9c77e65030c74b1c27c7d3ce066b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 17:00:33 -0300 Subject: [PATCH 045/107] refactor: remove globals, add values to config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/globals.go | 8 -------- cmd/stepsSoftServe.go | 2 +- configs/config.go | 5 +++++ 3 files changed, 6 insertions(+), 9 deletions(-) delete mode 100644 cmd/globals.go diff --git a/cmd/globals.go b/cmd/globals.go deleted file mode 100644 index d8a3f6cab..000000000 --- a/cmd/globals.go +++ /dev/null @@ -1,8 +0,0 @@ -package cmd - -//Common used strings by all commands -// todo: remove after Config (config package) is ready -//var home, kubectlClientPath, kubeconfigPath, localOs, localArchitecture, terraformPath, helmClientPath string - -//Should this be loaded from somewhere? -var installerEmail = "kubefirst-bot@kubefirst.com" diff --git a/cmd/stepsSoftServe.go b/cmd/stepsSoftServe.go index 04f62de77..68f2babdb 100644 --- a/cmd/stepsSoftServe.go +++ b/cmd/stepsSoftServe.go @@ -121,7 +121,7 @@ func configureSoftServe() { w.Commit("updating soft-serve server config", &git.CommitOptions{ Author: &object.Signature{ Name: "kubefirst-bot", - Email: installerEmail, + Email: config.InstallerEmail, When: time.Now(), }, }) diff --git a/configs/config.go b/configs/config.go index 470b44f94..fd5398fe2 100644 --- a/configs/config.go +++ b/configs/config.go @@ -8,6 +8,8 @@ import ( "runtime" ) +// Config host application configuration +// todo: some of these values can be moved to the .env type Config struct { AwsProfile string `env:"AWS_PROFILE"` KubefirstLogPath string `env:"KUBEFIRST_LOG_PATH" envDefault:"logs"` @@ -29,6 +31,7 @@ type Config struct { DestroyBuckets bool KubefirstVersion string + InstallerEmail string } func ReadConfig() *Config { @@ -60,5 +63,7 @@ func ReadConfig() *Config { config.KubefirstVersion = "0.1.1" + config.InstallerEmail = "kubefirst-bot@kubefirst.com" + return &config } From da767385ed104c9f5b8012b455ba0f75a052e638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 17:33:21 -0300 Subject: [PATCH 046/107] refactor: move git to internal 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/init.go | 71 +++------------------------------- cmd/kubefirstTemplate.go | 73 +---------------------------------- cmd/stepsArgo.go | 14 +++---- cmd/stepsMetaphor.go | 14 +++---- cmd/stepsSoftServe.go | 7 ++-- cmd/stepsUndefined.go | 36 ------------------ internal/gitClient/git.go | 77 +++++++++++++++++++++++++++++++++++++ pkg/keys.go | 80 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 182 insertions(+), 194 deletions(-) delete mode 100644 cmd/stepsUndefined.go create mode 100644 internal/gitClient/git.go create mode 100644 pkg/keys.go diff --git a/cmd/create.go b/cmd/create.go index cc681a4a9..e9fd8b59f 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -116,7 +116,7 @@ func init() { // todo: make this an optional switch and check for it or viper createCmd.Flags().Bool("destroy", false, "destroy resources") createCmd.PersistentFlags().BoolVarP(&config.DryRun, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") - createCmd.PersistentFlags().BoolVar(&skipVault, "skip-vault", false, "Skip post-git lab install and vault setup") - createCmd.PersistentFlags().BoolVar(&skipGitlab, "skip-gitlab", false, "Skip git lab install and vault setup") + createCmd.PersistentFlags().BoolVar(&skipVault, "skip-vault", false, "Skip post-gitClient lab install and vault setup") + createCmd.PersistentFlags().BoolVar(&skipGitlab, "skip-gitlab", false, "Skip gitClient lab install and vault setup") } diff --git a/cmd/init.go b/cmd/init.go index 47b968ba9..6fa5e7b01 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -5,12 +5,11 @@ import ( "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/internal/aws" "github.com/kubefirst/nebulous/internal/downloadManager" - "github.com/kubefirst/nebulous/internal/gitlab" + "github.com/kubefirst/nebulous/internal/gitClient" "github.com/kubefirst/nebulous/internal/telemetry" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" - "io/ioutil" "log" "strings" "time" @@ -101,13 +100,13 @@ to quickly create a Cobra application.`, } trackers[pkg.TrackerStage2].Tracker.Increment(1) - log.Println("calling createSshKeyPair() ") - createSshKeyPair() - log.Println("createSshKeyPair() complete") + log.Println("calling CreateSshKeyPair() ") + pkg.CreateSshKeyPair() + log.Println("CreateSshKeyPair() complete") trackers[pkg.TrackerStage3].Tracker.Increment(1) log.Println("calling cloneGitOpsRepo()") - cloneGitOpsRepo() + gitClient.CloneGitOpsRepo() log.Println("cloneGitOpsRepo() complete") trackers[pkg.TrackerStage4].Tracker.Increment(1) @@ -133,7 +132,6 @@ to quickly create a Cobra application.`, log.Println("detokenize() complete") trackers[pkg.TrackerStage8].Tracker.Increment(1) - // modConfigYaml() metricName = "kubefirst.init.completed" if !config.DryRun { @@ -180,62 +178,3 @@ func init() { initCmd.PersistentFlags().BoolVarP(&config.DryRun, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") log.Println("init started") } - -func createSshKeyPair() { - config := configs.ReadConfig() - publicKey := viper.GetString("botpublickey") - if publicKey == "" { - log.Println("generating new key pair") - publicKey, privateKey, _ := gitlab.GenerateKey() - viper.Set("botPublicKey", publicKey) - viper.Set("botPrivateKey", privateKey) - err := viper.WriteConfig() - if err != nil { - log.Panicf("error: could not write to viper config") - } - } - publicKey = viper.GetString("botpublickey") - privateKey := viper.GetString("botprivatekey") - - var argocdInitValuesYaml = []byte(fmt.Sprintf(` -server: - additionalApplications: - - name: registry - namespace: argocd - additionalLabels: {} - additionalAnnotations: {} - finalizers: - - resources-finalizer.argocd.argoproj.io - project: default - source: - repoURL: ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops - targetRevision: HEAD - path: registry - destination: - server: https://kubernetes.default.svc - namespace: argocd - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true -configs: - repositories: - soft-serve-gitops: - url: ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops - insecure: 'true' - type: git - name: soft-serve-gitops - credentialTemplates: - ssh-creds: - url: ssh://soft-serve.soft-serve.svc.cluster.local:22 - sshPrivateKey: | - %s -`, strings.ReplaceAll(privateKey, "\n", "\n "))) - - err := ioutil.WriteFile(fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", config.HomePath), argocdInitValuesYaml, 0644) - if err != nil { - log.Panicf("error: could not write argocd-init-values.yaml %s", err) - } -} diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 2948121b6..4b5527003 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -2,84 +2,13 @@ package cmd import ( "fmt" - "github.com/go-git/go-git/v5" - gitConfig "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing/object" - "github.com/kubefirst/nebulous/configs" "github.com/spf13/viper" - ssh2 "golang.org/x/crypto/ssh" "io/ioutil" - "log" "os" "path/filepath" "strings" - "time" ) -func cloneGitOpsRepo() { - - config := configs.ReadConfig() - url := "https://github.com/kubefirst/gitops-template" - directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) - - log.Println("git clone", url, directory) - - _, err := git.PlainClone(directory, false, &git.CloneOptions{ - URL: url, - }) - if err != nil { - log.Panicf("error cloning gitops-template repository from github, error is: %s", err) - } - - log.Println("downloaded gitops repo from template to directory", config.HomePath, "/.kubefirst/gitops") -} - -func pushGitopsToSoftServe() { - - config := configs.ReadConfig() - directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) - - log.Println("open git repo", directory) - - repo, err := git.PlainOpen(directory) - if err != nil { - log.Panicf("error opening the directory ", directory, err) - } - - log.Println("git remote add origin ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops") - _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ - Name: "soft", - URLs: []string{"ssh://127.0.0.1:8022/gitops"}, - }) - if err != nil { - log.Panicf("Error creating remote repo: %s", err) - } - w, _ := repo.Worktree() - - log.Println("Committing new changes...") - w.Add(".") - w.Commit("setting new remote upstream to soft-serve", &git.CommitOptions{ - Author: &object.Signature{ - Name: "kubefirst-bot", - Email: "kubefirst-bot@kubefirst.com", - When: time.Now(), - }, - }) - - auth, _ := publicKey() - - auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() - - err = repo.Push(&git.PushOptions{ - RemoteName: "soft", - Auth: auth, - }) - if err != nil { - log.Panicf("error pushing to remote", err) - } - -} - func detokenize(path string) { err := filepath.Walk(path, detokenizeDirectory) @@ -97,7 +26,7 @@ func detokenizeDirectory(path string, fi os.FileInfo, err error) error { return nil // } - if strings.Contains(path, ".git") || strings.Contains(path, ".terraform") { + if strings.Contains(path, ".gitClient") || strings.Contains(path, ".terraform") { return nil } diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go index 2fa342b04..7d01f631e 100644 --- a/cmd/stepsArgo.go +++ b/cmd/stepsArgo.go @@ -9,7 +9,7 @@ import ( "fmt" "github.com/ghodss/yaml" "github.com/go-git/go-git/v5" - gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/kubefirst/nebulous/configs" @@ -233,8 +233,8 @@ func gitlabKeyUpload() { } func pushGitopsToGitLab() { - config := configs.ReadConfig() - if config.DryRun { + cfg := configs.ReadConfig() + if cfg.DryRun { log.Printf("[#99] Dry-run mode, pushGitopsToGitLab skipped.") return } @@ -242,20 +242,18 @@ func pushGitopsToGitLab() { //TODO: should this step to be skipped if already executed? domain := viper.GetString("aws.hostedzonename") - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) - directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) + detokenize(fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath)) + directory := fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath) repo, err := git.PlainOpen(directory) if err != nil { log.Panicf("error opening the directory ", directory, err) } - //upstream := fmt.Sprintf("ssh://gitlab.%s:22:kubefirstVersion/gitops", viper.GetString("aws.hostedzonename")) - // upstream := "git@gitlab.kube1st.com:kubefirstVersion/gitops.git" upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) log.Println("git remote add gitlab at url", upstream) - _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ + _, err = repo.CreateRemote(&config.RemoteConfig{ Name: "gitlab", URLs: []string{upstream}, }) diff --git a/cmd/stepsMetaphor.go b/cmd/stepsMetaphor.go index dd85f093c..d19a022b2 100644 --- a/cmd/stepsMetaphor.go +++ b/cmd/stepsMetaphor.go @@ -3,7 +3,7 @@ package cmd import ( "fmt" "github.com/go-git/go-git/v5" - gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/kubefirst/nebulous/configs" @@ -13,15 +13,15 @@ import ( ) func hydrateGitlabMetaphorRepo() { - config := configs.ReadConfig() + cfg := configs.ReadConfig() //TODO: Should this be skipped if already executed? if !viper.GetBool("create.gitlabmetaphor.cloned") { - if config.DryRun { + if cfg.DryRun { log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") return } - metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", config.HomePath) + metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", cfg.HomePath) url := "https://github.com/kubefirst/metaphor-template" @@ -39,10 +39,10 @@ func hydrateGitlabMetaphorRepo() { // todo make global gitlabURL := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.hostedzonename")) - log.Println("git remote add origin", gitlabURL) - _, err = metaphorTemplateRepo.CreateRemote(&gitConfig.RemoteConfig{ + log.Println("gitClient remote add origin", gitlabURL) + _, err = metaphorTemplateRepo.CreateRemote(&config.RemoteConfig{ Name: "gitlab", - URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.git", gitlabURL)}, + URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.gitClient", gitlabURL)}, }) w, _ := metaphorTemplateRepo.Worktree() diff --git a/cmd/stepsSoftServe.go b/cmd/stepsSoftServe.go index 68f2babdb..1db047045 100644 --- a/cmd/stepsSoftServe.go +++ b/cmd/stepsSoftServe.go @@ -5,6 +5,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/gitClient" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" ssh2 "golang.org/x/crypto/ssh" @@ -71,7 +72,7 @@ func configureSoftserveAndPush() { time.Sleep(20 * time.Second) configureSoftServe() - pushGitopsToSoftServe() + gitClient.PushGitopsToSoftServe() viper.Set("create.softserve.configure", true) viper.WriteConfig() time.Sleep(30 * time.Second) @@ -86,9 +87,9 @@ func configureSoftServe() { url := "ssh://127.0.0.1:8022/config" directory := fmt.Sprintf("%s/.kubefirst/config", config.HomePath) - log.Println("git clone", url, directory) + log.Println("gitClient clone", url, directory) - auth, _ := publicKey() + auth, _ := pkg.PublicKey() auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() diff --git a/cmd/stepsUndefined.go b/cmd/stepsUndefined.go deleted file mode 100644 index 464b6cf1a..000000000 --- a/cmd/stepsUndefined.go +++ /dev/null @@ -1,36 +0,0 @@ -package cmd - - -import ( - "log" - "strings" - "github.com/spf13/viper" - "io/ioutil" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" -) - -func modConfigYaml() { - - file, err := ioutil.ReadFile("./config.yaml") - if err != nil { - log.Println("error reading file", err) - } - - newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) - - err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) - if err != nil { - panic(err) - } -} - - - -func publicKey() (*ssh.PublicKeys, error) { - var publicKey *ssh.PublicKeys - publicKey, err := ssh.NewPublicKeys("git", []byte(viper.GetString("botprivatekey")), "") - if err != nil { - return nil, err - } - return publicKey, err -} \ No newline at end of file diff --git a/internal/gitClient/git.go b/internal/gitClient/git.go new file mode 100644 index 000000000..d5883b59a --- /dev/null +++ b/internal/gitClient/git.go @@ -0,0 +1,77 @@ +package gitClient + +import ( + "fmt" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/pkg" + "golang.org/x/crypto/ssh" + "log" + "time" +) + +func CloneGitOpsRepo() { + + config := configs.ReadConfig() + url := "https://github.com/kubefirst/gitops-template" + directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) + + log.Println("gitClient clone", url, directory) + + _, err := git.PlainClone(directory, false, &git.CloneOptions{ + URL: url, + }) + if err != nil { + log.Panicf("error cloning gitops-template repository from github, error is: %s", err) + } + + log.Println("downloaded gitops repo from template to directory", config.HomePath, "/.kubefirst/gitops") +} + +func PushGitopsToSoftServe() { + + cfg := configs.ReadConfig() + directory := fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath) + + log.Println("open gitClient repo", directory) + + repo, err := git.PlainOpen(directory) + if err != nil { + log.Panicf("error opening the directory ", directory, err) + } + + log.Println("gitClient remote add origin ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops") + _, err = repo.CreateRemote(&config.RemoteConfig{ + Name: "soft", + URLs: []string{"ssh://127.0.0.1:8022/gitops"}, + }) + if err != nil { + log.Panicf("Error creating remote repo: %s", err) + } + w, _ := repo.Worktree() + + log.Println("Committing new changes...") + w.Add(".") + w.Commit("setting new remote upstream to soft-serve", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: "kubefirst-bot@kubefirst.com", + When: time.Now(), + }, + }) + + auth, _ := pkg.PublicKey() + + auth.HostKeyCallback = ssh.InsecureIgnoreHostKey() + + err = repo.Push(&git.PushOptions{ + RemoteName: "soft", + Auth: auth, + }) + if err != nil { + log.Panicf("error pushing to remote", err) + } + +} diff --git a/pkg/keys.go b/pkg/keys.go new file mode 100644 index 000000000..ac4043e8a --- /dev/null +++ b/pkg/keys.go @@ -0,0 +1,80 @@ +package pkg + +import ( + "fmt" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/gitlab" + "github.com/spf13/viper" + "io/ioutil" + "log" + "strings" +) + +func CreateSshKeyPair() { + config := configs.ReadConfig() + publicKey := viper.GetString("botpublickey") + if publicKey == "" { + log.Println("generating new key pair") + publicKey, privateKey, _ := gitlab.GenerateKey() + viper.Set("botPublicKey", publicKey) + viper.Set("botPrivateKey", privateKey) + err := viper.WriteConfig() + if err != nil { + log.Panicf("error: could not write to viper config") + } + } + publicKey = viper.GetString("botpublickey") + privateKey := viper.GetString("botprivatekey") + + var argocdInitValuesYaml = []byte(fmt.Sprintf(` +server: + additionalApplications: + - name: registry + namespace: argocd + additionalLabels: {} + additionalAnnotations: {} + finalizers: + - resources-finalizer.argocd.argoproj.io + project: default + source: + repoURL: ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops + targetRevision: HEAD + path: registry + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true +configs: + repositories: + soft-serve-gitops: + url: ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops + insecure: 'true' + type: gitClient + name: soft-serve-gitops + credentialTemplates: + ssh-creds: + url: ssh://soft-serve.soft-serve.svc.cluster.local:22 + sshPrivateKey: | + %s +`, strings.ReplaceAll(privateKey, "\n", "\n "))) + + err := ioutil.WriteFile(fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", config.HomePath), argocdInitValuesYaml, 0644) + if err != nil { + log.Panicf("error: could not write argocd-init-values.yaml %s", err) + } +} + +func PublicKey() (*ssh.PublicKeys, error) { + var publicKey *ssh.PublicKeys + publicKey, err := ssh.NewPublicKeys("gitClient", []byte(viper.GetString("botprivatekey")), "") + if err != nil { + return nil, err + } + return publicKey, err +} From 1145aee4dc2297bd62864df7e4e590f8d4b933cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 18:03:34 -0300 Subject: [PATCH 047/107] refactor: remove unused commands, refactor argocd and helm calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- README.md | 50 ++++ cmd/README.md | 46 ---- cmd/create.go | 31 ++- cmd/destroy.go | 15 +- cmd/init.go | 6 +- cmd/kubefirstTemplate.go | 95 ------- cmd/nebulous.go | 41 --- cmd/stepsArgo.go | 543 -------------------------------------- cmd/stepsBaseInstall.go | 4 +- cmd/stepsMetaphor.go | 3 +- cmd/version.go | 4 - configs/config.go | 3 + internal/argocd/argocd.go | 89 +++++++ internal/gitlab/gitlab.go | 294 +++++++++++++++++++++ internal/helm/helm.go | 50 ++++ pkg/helpers.go | 214 +++++++++++++++ 16 files changed, 729 insertions(+), 759 deletions(-) delete mode 100644 cmd/README.md delete mode 100644 cmd/kubefirstTemplate.go delete mode 100644 cmd/nebulous.go delete mode 100644 cmd/stepsArgo.go create mode 100644 internal/helm/helm.go create mode 100644 pkg/helpers.go diff --git a/README.md b/README.md index 87c71ee84..fe05c74ed 100644 --- a/README.md +++ b/README.md @@ -211,3 +211,53 @@ rm ~/.flare added gitlab.yaml to registry pushing local to soft origin + +--- +todo: this is coming from cmd/README.md , we need to format it: + +# Overview + +General overview of the code, to help shuffling parts around. + +# Globals Variables + +| Variable|Type | Use | +|:--|:--|:--| +|cleanCmd|Command| Clean command| +|createCmd|Command| Create command| +|destroyCmd|Command| Destroy command| +|nebulousCmd|Command| CLI main command | +|versionCmd|Command| Version command| +|rootCmd|Command| Root command command| +|infoCmd|Command| Info command| +|initCmd|Command| Init command - pre-provision command| +|home|String|User Home dir or Work dir| +|kubectlClientPath|String|Kubectl CLI path| +|kubeconfigPath|String|Kubeconfig Path| +|localOs|String|Host OS| +|localArchitecture|String|Host OS architecture| +|terraformPath|String|Terraform CLI path| +|helmClientPath|String|Helm CLI path| +|dryrunMode|Bool|If installer should run in dry-run mode| +|Trackers|Trecker|Map of trackers| +|vaultRootToken|String|Root token for vault| +|gitlabToolboxPodName|String|Toolbox pod name| +|gitlabSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| +|vaultSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| +|argocdSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| +|gitlabSecretClient|coreV1Types.PodInterface|Client shorthand to interface| +|cfgFile|String| .flare config file| +|NebolousVersion|String|CLI version| + + +# Commands Available + +| Command | Short Description | Long Description| +|:---|:---|:---| +|checktools|Present, needs review.|Present, needs review.| +|clean|Missing Text|Missing Text| +|create|Missing Text|Missing Text| +|destroy|Missing Text|Missing Text| +|info|Present, needs review.|Present, needs review.| +|init|Missing Text|Missing Text| +|version|Present, needs review.|Present, needs review.| diff --git a/cmd/README.md b/cmd/README.md deleted file mode 100644 index 219d5a4bc..000000000 --- a/cmd/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Overview - -General overview of the code, to help shuffling parts around. - -# Globals Variables - -| Variable|Type | Use | -|:--|:--|:--| -|cleanCmd|Command| Clean command| -|createCmd|Command| Create command| -|destroyCmd|Command| Destroy command| -|nebulousCmd|Command| CLI main command | -|versionCmd|Command| Version command| -|rootCmd|Command| Root command command| -|infoCmd|Command| Info command| -|initCmd|Command| Init command - pre-provision command| -|home|String|User Home dir or Work dir| -|kubectlClientPath|String|Kubectl CLI path| -|kubeconfigPath|String|Kubeconfig Path| -|localOs|String|Host OS| -|localArchitecture|String|Host OS architecture| -|terraformPath|String|Terraform CLI path| -|helmClientPath|String|Helm CLI path| -|dryrunMode|Bool|If installer should run in dry-run mode| -|Trackers|Trecker|Map of trackers| -|vaultRootToken|String|Root token for vault| -|gitlabToolboxPodName|String|Toolbox pod name| -|gitlabSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| -|vaultSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| -|argocdSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| -|gitlabSecretClient|coreV1Types.PodInterface|Client shorthand to interface| -|cfgFile|String| .flare config file| -|NebolousVersion|String|CLI version| - - -# Commands Available - -| Command | Short Description | Long Description| -|:---|:---|:---| -|checktools|Present, needs review.|Present, needs review.| -|clean|Missing Text|Missing Text| -|create|Missing Text|Missing Text| -|destroy|Missing Text|Missing Text| -|info|Present, needs review.|Present, needs review.| -|init|Missing Text|Missing Text| -|version|Present, needs review.|Present, needs review.| \ No newline at end of file diff --git a/cmd/create.go b/cmd/create.go index e9fd8b59f..faec27ea6 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -3,6 +3,9 @@ package cmd import ( "fmt" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/argocd" + "github.com/kubefirst/nebulous/internal/gitlab" + "github.com/kubefirst/nebulous/internal/helm" "github.com/kubefirst/nebulous/internal/telemetry" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" @@ -59,18 +62,18 @@ to quickly create a Cobra application.`, Trackers[trackerStage21].Tracker.Increment(int64(1)) configureSoftserveAndPush() Trackers[trackerStage21].Tracker.Increment(int64(1)) - helmInstallArgocd(config.HomePath) + helm.InstallArgocd(config.HomePath) Trackers[trackerStage22].Tracker.Increment(int64(1)) if !skipGitlab { //TODO: Confirm if we need to waitgit lab to be ready // OR something, too fast the secret will not be there. - awaitGitlab() - produceGitlabTokens() + gitlab.AwaitGitlab() + gitlab.ProduceGitlabTokens() Trackers[trackerStage22].Tracker.Increment(int64(1)) - applyGitlabTerraform(directory) + gitlab.ApplyGitlabTerraform(directory) Trackers[trackerStage22].Tracker.Increment(int64(1)) - gitlabKeyUpload() + gitlab.GitlabKeyUpload() Trackers[trackerStage22].Tracker.Increment(int64(1)) if !skipVault { @@ -78,23 +81,23 @@ to quickly create a Cobra application.`, Trackers[trackerStage23].Tracker.Increment(int64(1)) addGitlabOidcApplications() Trackers[trackerStage23].Tracker.Increment(int64(1)) - awaitGitlab() + gitlab.AwaitGitlab() Trackers[trackerStage22].Tracker.Increment(int64(1)) - pushGitopsToGitLab() + gitlab.PushGitOpsToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) - changeRegistryToGitLab() + pkg.ChangeRegistryToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) hydrateGitlabMetaphorRepo() Trackers[trackerStage23].Tracker.Increment(int64(1)) - token := getArgocdAuthToken() - syncArgocdApplication("argo-components", token) - syncArgocdApplication("gitlab-runner-components", token) - syncArgocdApplication("gitlab-runner", token) - syncArgocdApplication("atlantis-components", token) - syncArgocdApplication("chartmuseum-components", token) + token := argocd.GetArgocdAuthToken() + argocd.SyncArgocdApplication("argo-components", token) + argocd.SyncArgocdApplication("gitlab-runner-components", token) + argocd.SyncArgocdApplication("gitlab-runner", token) + argocd.SyncArgocdApplication("atlantis-components", token) + argocd.SyncArgocdApplication("chartmuseum-components", token) } } diff --git a/cmd/destroy.go b/cmd/destroy.go index ed6a1a1cc..90077281a 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -3,6 +3,7 @@ package cmd import ( "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/internal/aws" + "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/k8s" "github.com/spf13/cobra" "log" @@ -11,12 +12,6 @@ import ( "syscall" ) -var skipGitlabTerraform bool - -//var skipDeleteRegistryApplication bool -var skipBaseTerraform bool -var DestroyBuckets bool - // destroyCmd represents the destroy command var destroyCmd = &cobra.Command{ Use: "destroy", @@ -39,7 +34,7 @@ if the registry has already been delteted.`, log.Panicf("error: failed to port-forward to gitlab %s", err) } // todo this needs to be removed when we are no longer in the starter account - destroyGitlabTerraform() + gitlab.DestroyGitlabTerraform() // delete argocd registry k8s.DeleteRegistryApplication() destroyBaseTerraform() @@ -53,8 +48,8 @@ func init() { rootCmd.AddCommand(destroyCmd) - destroyCmd.PersistentFlags().BoolVar(&skipGitlabTerraform, "skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") + destroyCmd.PersistentFlags().BoolVar(&config.SkipGitlabTerraform, "skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") destroyCmd.PersistentFlags().BoolVar(&config.SkipDeleteRegistryApplication, "skip-delete-register", false, "whether to skip deletion of resgister application ") - destroyCmd.PersistentFlags().BoolVar(&skipBaseTerraform, "skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") - destroyCmd.PersistentFlags().BoolVar(&DestroyBuckets, "destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") + destroyCmd.PersistentFlags().BoolVar(&config.SkipBaseTerraform, "skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") + destroyCmd.PersistentFlags().BoolVar(&config.DestroyBuckets, "destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") } diff --git a/cmd/init.go b/cmd/init.go index 6fa5e7b01..6697967fc 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -127,9 +127,9 @@ to quickly create a Cobra application.`, aws.BucketRand() log.Println("BucketRand() complete") - log.Println("calling detokenize()") - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) - log.Println("detokenize() complete") + log.Println("calling Detokenize()") + pkg.Detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) + log.Println("Detokenize() complete") trackers[pkg.TrackerStage8].Tracker.Increment(1) metricName = "kubefirst.init.completed" diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go deleted file mode 100644 index 4b5527003..000000000 --- a/cmd/kubefirstTemplate.go +++ /dev/null @@ -1,95 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/spf13/viper" - "io/ioutil" - "os" - "path/filepath" - "strings" -) - -func detokenize(path string) { - - err := filepath.Walk(path, detokenizeDirectory) - if err != nil { - panic(err) - } -} - -func detokenizeDirectory(path string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - - if fi.IsDir() { - return nil // - } - - if strings.Contains(path, ".gitClient") || strings.Contains(path, ".terraform") { - return nil - } - - matched, err := filepath.Match("*", fi.Name()) - - if err != nil { - panic(err) - } - - if matched { - read, err := ioutil.ReadFile(path) - if err != nil { - panic(err) - } - // todo should detokenize be a switch statement based on a value found in viper? - gitlabConfigured := viper.GetBool("gitlab.keyuploaded") - - newContents := "" - - if gitlabConfigured { - newContents = strings.Replace(string(read), "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")), -1) - } else { - newContents = strings.Replace(string(read), "https://gitlab./kubefirst/gitops.git", "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", -1) - } - - botPublicKey := viper.GetString("botpublickey") - domainId := viper.GetString("aws.domainid") - hostedzonename := viper.GetString("aws.hostedzonename") - bucketStateStore := viper.GetString("bucket.state-store.name") - bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") - bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") - bucketChartmuseum := viper.GetString("bucket.chartmuseum.name") - region := viper.GetString("aws.region") - adminEmail := viper.GetString("adminemail") - awsAccountId := viper.GetString("aws.accountid") - kmsKeyId := viper.GetString("vault.kmskeyid") - - newContents = strings.Replace(newContents, "", strings.TrimSpace(botPublicKey), -1) - newContents = strings.Replace(newContents, "", bucketStateStore, -1) - newContents = strings.Replace(newContents, "", bucketArgoArtifacts, -1) - newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) - newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) - newContents = strings.Replace(newContents, "", domainId, -1) - newContents = strings.Replace(newContents, "", hostedzonename, -1) - newContents = strings.Replace(newContents, "", region, -1) - newContents = strings.Replace(newContents, "", adminEmail, -1) - newContents = strings.Replace(newContents, "", awsAccountId, -1) - if kmsKeyId != "" { - newContents = strings.Replace(newContents, "", kmsKeyId, -1) - } - - if viper.GetBool("create.terraformapplied.gitlab") { - newContents = strings.Replace(newContents, "", hostedzonename, -1) - newContents = strings.Replace(newContents, "", region, -1) - newContents = strings.Replace(newContents, "", awsAccountId, -1) - } - - err = ioutil.WriteFile(path, []byte(newContents), 0) - if err != nil { - panic(err) - } - - } - - return nil -} diff --git a/cmd/nebulous.go b/cmd/nebulous.go deleted file mode 100644 index 6449ab4ae..000000000 --- a/cmd/nebulous.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright © 2022 NAME HERE - -*/ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -// nebulousCmd represents the nebulous command -var nebulousCmd = &cobra.Command{ - Use: "nebulous", - Short: "A brief description of your command", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("nebulous called") - - }, -} - -func init() { - rootCmd.AddCommand(nebulousCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // nebulousCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // nebulousCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} diff --git a/cmd/stepsArgo.go b/cmd/stepsArgo.go deleted file mode 100644 index 7d01f631e..000000000 --- a/cmd/stepsArgo.go +++ /dev/null @@ -1,543 +0,0 @@ -package cmd - -import ( - "bytes" - "context" - "crypto/tls" - b64 "encoding/base64" - "encoding/json" - "fmt" - "github.com/ghodss/yaml" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing/object" - gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/gitlab" - "github.com/kubefirst/nebulous/internal/k8s" - "github.com/kubefirst/nebulous/pkg" - "github.com/spf13/viper" - "html/template" - "io/ioutil" - v1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "log" - "net/http" - "net/url" - "os" - "os/exec" - "strings" - "syscall" - "time" -) - -func helmInstallArgocd(home string) { - config := configs.ReadConfig() - if !viper.GetBool("create.argocd.helm") { - if config.DryRun { - log.Printf("[#99] Dry-run mode, helmInstallArgocd skipped.") - return - } - // ! commenting out until a clean execution is necessary // create namespace - helmRepoAddArgocd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") - helmRepoAddArgocd.Stdout = os.Stdout - helmRepoAddArgocd.Stderr = os.Stderr - err := helmRepoAddArgocd.Run() - if err != nil { - log.Panicf("error: could not run helm repo add %s", err) - } - - helmRepoUpdate := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "update") - helmRepoUpdate.Stdout = os.Stdout - helmRepoUpdate.Stderr = os.Stderr - err = helmRepoUpdate.Run() - if err != nil { - log.Panicf("error: could not helm repo update %s", err) - } - - helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") - helmInstallArgocdCmd.Stdout = os.Stdout - helmInstallArgocdCmd.Stderr = os.Stderr - err = helmInstallArgocdCmd.Run() - if err != nil { - log.Panicf("error: could not helm install argocd command %s", err) - } - - viper.Set("create.argocd.helm", true) - err = viper.WriteConfig() - if err != nil { - log.Panicf("error: could not write to viper config") - } - } -} - -func awaitGitlab() { - config := configs.ReadConfig() - - log.Println("awaitGitlab called") - if config.DryRun { - log.Printf("[#99] Dry-run mode, awaitGitlab skipped.") - return - } - max := 200 - for i := 0; i < max; i++ { - hostedZoneName := viper.GetString("aws.hostedzonename") - resp, _ := http.Get(fmt.Sprintf("https://gitlab.%s", hostedZoneName)) - if resp != nil && resp.StatusCode == 200 { - log.Println("gitlab host resolved, 30 second grace period required...") - time.Sleep(time.Second * 30) - i = max - } else { - log.Println("gitlab host not resolved, sleeping 10s") - time.Sleep(time.Second * 10) - } - } -} - -func produceGitlabTokens() { - //TODO: Should this step be skipped if already executed? - config := configs.ReadConfig() - k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) - if err != nil { - panic(err.Error()) - } - clientset, err := kubernetes.NewForConfig(k8sConfig) - if err != nil { - panic(err.Error()) - } - log.Println("discovering gitlab toolbox pod") - if config.DryRun { - log.Printf("[#99] Dry-run mode, produceGitlabTokens skipped.") - return - } - time.Sleep(30 * time.Second) - // todo: move it to config - k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") - - argocdPassword := k8s.GetSecretValue(k8s.ArgocdSecretClient, "argocd-initial-admin-secret", "password") - - viper.Set("argocd.admin.password", argocdPassword) - viper.WriteConfig() - - log.Println("discovering gitlab toolbox pod") - - k8s.GitlabPodsClient = clientset.CoreV1().Pods("gitlab") - gitlabPodName := k8s.GetPodNameByLabel(k8s.GitlabPodsClient, "toolbox") - - k8s.GitlabSecretClient = clientset.CoreV1().Secrets("gitlab") - secrets, err := k8s.GitlabSecretClient.List(context.TODO(), metaV1.ListOptions{}) - - var gitlabRootPasswordSecretName string - - for _, secret := range secrets.Items { - if strings.Contains(secret.Name, "initial-root-password") { - gitlabRootPasswordSecretName = secret.Name - log.Println("gitlab initial root password secret name: ", gitlabRootPasswordSecretName) - } - } - gitlabRootPassword := k8s.GetSecretValue(k8s.GitlabSecretClient, gitlabRootPasswordSecretName, "password") - - viper.Set("gitlab.podname", gitlabPodName) - viper.Set("gitlab.root.password", gitlabRootPassword) - viper.WriteConfig() - - gitlabToken := viper.GetString("gitlab.token") - - if gitlabToken == "" { - - log.Println("generating gitlab personal access token") - gitlab.GitlabGeneratePersonalAccessToken(gitlabPodName) - - } - - gitlabRunnerToken := viper.GetString("gitlab.runnertoken") - - if gitlabRunnerToken == "" { - - log.Println("getting gitlab runner token") - gitlabRunnerRegistrationToken := k8s.GetSecretValue(k8s.GitlabSecretClient, "gitlab-gitlab-runner-secret", "runner-registration-token") - viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) - viper.WriteConfig() - } - -} - -func applyGitlabTerraform(directory string) { - config := configs.ReadConfig() - if !viper.GetBool("create.terraformapplied.gitlab") { - log.Println("Executing applyGitlabTerraform") - if config.DryRun { - log.Printf("[#99] Dry-run mode, applyGitlabTerraform skipped.") - return - } - // Prepare for terraform gitlab execution - os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) - os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") - - directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) - err := os.Chdir(directory) - if err != nil { - log.Panic("error: could not change directory to " + directory) - } - _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") - if errInit != nil { - panic(fmt.Sprintf("error: terraform init for gitlab failed %s", err)) - } - _, _, errApply := pkg.ExecShellReturnStrings(config.TerraformPath, "apply", "-auto-approve") - if errApply != nil { - panic(fmt.Sprintf("error: terraform apply for gitlab failed %s", err)) - } - os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) - viper.Set("create.terraformapplied.gitlab", true) - viper.WriteConfig() - } else { - log.Println("Skipping: applyGitlabTerraform") - } -} - -func gitlabKeyUpload() { - config := configs.ReadConfig() - // upload ssh public key - if !viper.GetBool("gitlab.keyuploaded") { - log.Println("Executing gitlabKeyUpload") - log.Println("uploading ssh public key for gitlab user") - if config.DryRun { - log.Printf("[#99] Dry-run mode, gitlabKeyUpload skipped.") - return - } - log.Println("uploading ssh public key to gitlab") - gitlabToken := viper.GetString("gitlab.token") - data := url.Values{ - "title": {"kubefirst"}, - "key": {viper.GetString("botpublickey")}, - } - - gitlabUrlBase := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) - - resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) - if err != nil { - log.Fatal(err) - } - var res map[string]interface{} - json.NewDecoder(resp.Body).Decode(&res) - log.Println(res) - log.Println("ssh public key uploaded to gitlab") - viper.Set("gitlab.keyuploaded", true) - viper.WriteConfig() - } else { - log.Println("Skipping: gitlabKeyUpload") - log.Println("ssh public key already uploaded to gitlab") - } -} - -func pushGitopsToGitLab() { - cfg := configs.ReadConfig() - if cfg.DryRun { - log.Printf("[#99] Dry-run mode, pushGitopsToGitLab skipped.") - return - } - - //TODO: should this step to be skipped if already executed? - domain := viper.GetString("aws.hostedzonename") - - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath)) - directory := fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath) - - repo, err := git.PlainOpen(directory) - if err != nil { - log.Panicf("error opening the directory ", directory, err) - } - - upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) - log.Println("git remote add gitlab at url", upstream) - - _, err = repo.CreateRemote(&config.RemoteConfig{ - Name: "gitlab", - URLs: []string{upstream}, - }) - if err != nil { - log.Println("Error creating remote repo:", err) - } - w, _ := repo.Worktree() - - os.RemoveAll(directory + "/terraform/base/.terraform") - os.RemoveAll(directory + "/terraform/gitlab/.terraform") - os.RemoveAll(directory + "/terraform/vault/.terraform") - - log.Println("Committing new changes...") - w.Add(".") - _, err = w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ - Author: &object.Signature{ - Name: "kubefirst-bot", - Email: "kubefirst-bot@kubefirst.com", - When: time.Now(), - }, - }) - if err != nil { - log.Panicf("error committing changes", err) - } - - log.Println("setting auth...") - // auth, _ := publicKey() - // auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() - - auth := &gitHttp.BasicAuth{ - Username: "root", - Password: viper.GetString("gitlab.token"), - } - - err = repo.Push(&git.PushOptions{ - RemoteName: "gitlab", - Auth: auth, - }) - if err != nil { - log.Panicf("error pushing to remote", err) - } - -} - -func changeRegistryToGitLab() { - config := configs.ReadConfig() - if !viper.GetBool("gitlab.registry") { - if config.DryRun { - log.Printf("[#99] Dry-run mode, changeRegistryToGitLab skipped.") - return - } - - type ArgocdGitCreds struct { - PersonalAccessToken string - URL string - FullURL string - } - - pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) - url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.hostedzonename")))) - fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")))) - - creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} - - var argocdRepositoryAccessTokenSecret *v1.Secret - k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) - if err != nil { - log.Panicf("error getting client from kubeconfig") - } - clientset, err := kubernetes.NewForConfig(k8sConfig) - if err != nil { - log.Panicf("error getting kubeconfig for clientset") - } - k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") - - var secrets bytes.Buffer - - c, err := template.New("creds-gitlab").Parse(` - apiVersion: v1 - data: - password: {{ .PersonalAccessToken }} - url: {{ .URL }} - username: cm9vdA== - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repo-creds - name: creds-gitlab - namespace: argocd - type: Opaque - `) - if err := c.Execute(&secrets, creds); err != nil { - log.Panicf("error executing golang template for git repository credentials template %s", err) - } - - ba := []byte(secrets.String()) - err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - - _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) - if err != nil { - log.Panicf("error creating argocd repository credentials template secret %s", err) - } - - var repoSecrets bytes.Buffer - - c, err = template.New("repo-gitlab").Parse(` - apiVersion: v1 - data: - project: ZGVmYXVsdA== - type: Z2l0 - url: {{ .FullURL }} - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repository - name: repo-gitlab - namespace: argocd - type: Opaque - `) - if err := c.Execute(&repoSecrets, creds); err != nil { - log.Panicf("error executing golang template for gitops repository template %s", err) - } - - ba = []byte(repoSecrets.String()) - err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - - _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) - if err != nil { - log.Panicf("error creating argocd repository connection secret %s", err) - } - - k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.HomePath)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err = k.Run() - if err != nil { - log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) - } - - viper.Set("gitlab.registry", true) - viper.WriteConfig() - } else { - log.Println("Skipping: changeRegistryToGitLab") - } -} - -func getArgocdAuthToken() string { - config := configs.ReadConfig() - if config.DryRun { - log.Printf("[#99] Dry-run mode, getArgocdAuthToken skipped.") - return "nothing" - } - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to argocd %s", err) - } - - url := "https://localhost:8080/api/v1/session" - - payload := strings.NewReader(fmt.Sprintf("{\n\t\"username\":\"admin\",\"password\":\"%s\"\n}", viper.GetString("argocd.admin.password"))) - - http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - req, err := http.NewRequest("POST", url, payload) - if err != nil { - log.Fatal("error getting auth token from argocd ", err) - } - - client := &http.Client{ - CheckRedirect: func(req *http.Request, via []*http.Request) error { - // N.B.: when used in production, also check for redirect loops - return nil - }, - } - - res, err := client.Do(req) - if err != nil { - log.Fatal("error requesting auth token from argocd") - } - - defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Fatal("error sending POST request to get argocd auth token :", err) - } - - var dat map[string]interface{} - - if err := json.Unmarshal(body, &dat); err != nil { - log.Panicf("error unmarshalling %s", err) - } - token := dat["token"] - viper.Set("argocd.admin.apitoken", token) - viper.WriteConfig() - - return token.(string) - -} - -func syncArgocdApplication(applicationName, argocdAuthToken string) { - config := configs.ReadConfig() - if config.DryRun { - log.Printf("[#99] Dry-run mode, syncArgocdApplication skipped.") - return - } - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to argocd %s", err) - } - - // todo need to replace this with a curl wrapper and see if it WORKS - - url := fmt.Sprintf("https://localhost:8080/api/v1/applications/%s/sync", applicationName) - - argoCdAppSync := exec.Command("curl", "-k", "-L", "-X", "POST", url, "-H", fmt.Sprintf("Authorization: Bearer %s", argocdAuthToken)) - argoCdAppSync.Stdout = os.Stdout - argoCdAppSync.Stderr = os.Stderr - err = argoCdAppSync.Run() - if err != nil { - log.Panicf("error: curl appSync failed failed %s", err) - } -} - -func destroyGitlabTerraform() { - config := configs.ReadConfig() - log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") - // kubeconfig := os.Getenv("HOME") + "/.kube/config" - // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) - // argocdclientset, err := argocdclientset.NewForConfig(config) - // if err != nil { - // return nil, err - // } - - //* should we git clone the gitops repo when destroy is run back to their - //* local host to get the latest values of gitops - - os.Setenv("AWS_REGION", viper.GetString("aws.region")) - os.Setenv("AWS_ACCOUNT_ID", viper.GetString("aws.accountid")) - os.Setenv("HOSTED_ZONE_NAME", viper.GetString("aws.hostedzonename")) - os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) - - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) - err := os.Chdir(directory) - if err != nil { - log.Panicf("error: could not change directory to " + directory) - } - - os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") - - if !skipGitlabTerraform { - tfInitGitlabCmd := exec.Command(config.TerraformPath, "init") - tfInitGitlabCmd.Stdout = os.Stdout - tfInitGitlabCmd.Stderr = os.Stderr - err = tfInitGitlabCmd.Run() - if err != nil { - log.Panicf("failed to terraform init gitlab %s", err) - } - - tfDestroyGitlabCmd := exec.Command(config.TerraformPath, "destroy", "-auto-approve") - tfDestroyGitlabCmd.Stdout = os.Stdout - tfDestroyGitlabCmd.Stderr = os.Stderr - err = tfDestroyGitlabCmd.Run() - if err != nil { - log.Panicf("failed to terraform destroy gitlab %s", err) - } - - viper.Set("destroy.terraformdestroy.gitlab", true) - viper.WriteConfig() - } else { - log.Println("skip: destroyGitlabTerraform") - } -} diff --git a/cmd/stepsBaseInstall.go b/cmd/stepsBaseInstall.go index cfc92e31f..6c7cbc6c8 100644 --- a/cmd/stepsBaseInstall.go +++ b/cmd/stepsBaseInstall.go @@ -47,7 +47,7 @@ func applyBaseTerraform(cmd *cobra.Command, directory string) { viper.Set("vault.kmskeyid", keyId) viper.Set("create.terraformapplied.base", true) viper.WriteConfig() - detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) + pkg.Detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) } else { log.Println("Skipping: ApplyBaseTerraform") } @@ -55,7 +55,7 @@ func applyBaseTerraform(cmd *cobra.Command, directory string) { func destroyBaseTerraform() { config := configs.ReadConfig() - if !skipBaseTerraform { + if !config.SkipBaseTerraform { directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) err := os.Chdir(directory) if err != nil { diff --git a/cmd/stepsMetaphor.go b/cmd/stepsMetaphor.go index d19a022b2..7697fc0ab 100644 --- a/cmd/stepsMetaphor.go +++ b/cmd/stepsMetaphor.go @@ -7,6 +7,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" "log" "time" @@ -33,7 +34,7 @@ func hydrateGitlabMetaphorRepo() { } viper.Set("create.gitlabmetaphor.cloned", true) - detokenize(metaphorTemplateDir) + pkg.Detokenize(metaphorTemplateDir) viper.Set("create.gitlabmetaphor.detokenized", true) diff --git a/cmd/version.go b/cmd/version.go index ec6c09117..01644bd04 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,7 +1,3 @@ -/* -Copyright © 2022 NAME HERE - -*/ package cmd import ( diff --git a/configs/config.go b/configs/config.go index fd5398fe2..8b6be99a3 100644 --- a/configs/config.go +++ b/configs/config.go @@ -32,6 +32,9 @@ type Config struct { KubefirstVersion string InstallerEmail string + + SkipGitlabTerraform bool + SkipBaseTerraform bool } func ReadConfig() *Config { diff --git a/internal/argocd/argocd.go b/internal/argocd/argocd.go index 9cadc6e15..717a5362a 100644 --- a/internal/argocd/argocd.go +++ b/internal/argocd/argocd.go @@ -6,10 +6,15 @@ import ( "encoding/json" "errors" "fmt" + "github.com/kubefirst/nebulous/configs" "github.com/spf13/viper" "io/ioutil" "log" "net/http" + "os" + "os/exec" + "strings" + "syscall" ) // kSyncArgocdApplication request ArgoCD to manual sync an application. Expected parameters are the ArgoCD application @@ -112,3 +117,87 @@ func getArgoCDToken(username string, password string) (string, error) { return token, nil } + +func GetArgocdAuthToken() string { + config := configs.ReadConfig() + if config.DryRun { + log.Printf("[#99] Dry-run mode, GetArgocdAuthToken skipped.") + return "nothing" + } + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd %s", err) + } + + url := "https://localhost:8080/api/v1/session" + + payload := strings.NewReader(fmt.Sprintf("{\n\t\"username\":\"admin\",\"password\":\"%s\"\n}", viper.GetString("argocd.admin.password"))) + + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + req, err := http.NewRequest("POST", url, payload) + if err != nil { + log.Fatal("error getting auth token from argocd ", err) + } + + client := &http.Client{ + CheckRedirect: func(req *http.Request, via []*http.Request) error { + // N.B.: when used in production, also check for redirect loops + return nil + }, + } + + res, err := client.Do(req) + if err != nil { + log.Fatal("error requesting auth token from argocd") + } + + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Fatal("error sending POST request to get argocd auth token :", err) + } + + var dat map[string]interface{} + + if err := json.Unmarshal(body, &dat); err != nil { + log.Panicf("error unmarshalling %s", err) + } + token := dat["token"] + viper.Set("argocd.admin.apitoken", token) + viper.WriteConfig() + + return token.(string) + +} + +func SyncArgocdApplication(applicationName, argocdAuthToken string) { + config := configs.ReadConfig() + if config.DryRun { + log.Printf("[#99] Dry-run mode, SyncArgocdApplication skipped.") + return + } + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err := kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd %s", err) + } + + // todo need to replace this with a curl wrapper and see if it WORKS + + url := fmt.Sprintf("https://localhost:8080/api/v1/applications/%s/sync", applicationName) + + argoCdAppSync := exec.Command("curl", "-k", "-L", "-X", "POST", url, "-H", fmt.Sprintf("Authorization: Bearer %s", argocdAuthToken)) + argoCdAppSync.Stdout = os.Stdout + argoCdAppSync.Stderr = os.Stderr + err = argoCdAppSync.Run() + if err != nil { + log.Panicf("error: curl appSync failed failed %s", err) + } +} diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index dd69e94b6..eeacb0588 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -1,18 +1,33 @@ package gitlab import ( + "context" "crypto/rand" "crypto/rsa" "crypto/x509" + "encoding/json" "encoding/pem" "fmt" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/google/uuid" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/k8s" + "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" "log" + "net/http" + "net/url" "os" "os/exec" + "strings" "syscall" + "time" "golang.org/x/crypto/ssh" ) @@ -72,3 +87,282 @@ func GitlabGeneratePersonalAccessToken(gitlabPodName string) { log.Println("gitlab personal access token generated", gitlabToken) } + +func PushGitOpsToGitLab() { + cfg := configs.ReadConfig() + if cfg.DryRun { + log.Printf("[#99] Dry-run mode, PushGitOpsToGitLab skipped.") + return + } + + //TODO: should this step to be skipped if already executed? + domain := viper.GetString("aws.hostedzonename") + + pkg.Detokenize(fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath)) + directory := fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath) + + repo, err := git.PlainOpen(directory) + if err != nil { + log.Panicf("error opening the directory ", directory, err) + } + + upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) + log.Println("git remote add gitlab at url", upstream) + + _, err = repo.CreateRemote(&config.RemoteConfig{ + Name: "gitlab", + URLs: []string{upstream}, + }) + if err != nil { + log.Println("Error creating remote repo:", err) + } + w, _ := repo.Worktree() + + os.RemoveAll(directory + "/terraform/base/.terraform") + os.RemoveAll(directory + "/terraform/gitlab/.terraform") + os.RemoveAll(directory + "/terraform/vault/.terraform") + + log.Println("Committing new changes...") + w.Add(".") + _, err = w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: "kubefirst-bot@kubefirst.com", + When: time.Now(), + }, + }) + if err != nil { + log.Panicf("error committing changes", err) + } + + log.Println("setting auth...") + // auth, _ := publicKey() + // auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + auth := &gitHttp.BasicAuth{ + Username: "root", + Password: viper.GetString("gitlab.token"), + } + + err = repo.Push(&git.PushOptions{ + RemoteName: "gitlab", + Auth: auth, + }) + if err != nil { + log.Panicf("error pushing to remote", err) + } + +} + +func AwaitGitlab() { + config := configs.ReadConfig() + + log.Println("AwaitGitlab called") + if config.DryRun { + log.Printf("[#99] Dry-run mode, AwaitGitlab skipped.") + return + } + max := 200 + for i := 0; i < max; i++ { + hostedZoneName := viper.GetString("aws.hostedzonename") + resp, _ := http.Get(fmt.Sprintf("https://gitlab.%s", hostedZoneName)) + if resp != nil && resp.StatusCode == 200 { + log.Println("gitlab host resolved, 30 second grace period required...") + time.Sleep(time.Second * 30) + i = max + } else { + log.Println("gitlab host not resolved, sleeping 10s") + time.Sleep(time.Second * 10) + } + } +} + +func ProduceGitlabTokens() { + //TODO: Should this step be skipped if already executed? + config := configs.ReadConfig() + k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) + if err != nil { + panic(err.Error()) + } + clientset, err := kubernetes.NewForConfig(k8sConfig) + if err != nil { + panic(err.Error()) + } + log.Println("discovering gitlab toolbox pod") + if config.DryRun { + log.Printf("[#99] Dry-run mode, ProduceGitlabTokens skipped.") + return + } + time.Sleep(30 * time.Second) + // todo: move it to config + k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") + + argocdPassword := k8s.GetSecretValue(k8s.ArgocdSecretClient, "argocd-initial-admin-secret", "password") + + viper.Set("argocd.admin.password", argocdPassword) + viper.WriteConfig() + + log.Println("discovering gitlab toolbox pod") + + k8s.GitlabPodsClient = clientset.CoreV1().Pods("gitlab") + gitlabPodName := k8s.GetPodNameByLabel(k8s.GitlabPodsClient, "toolbox") + + k8s.GitlabSecretClient = clientset.CoreV1().Secrets("gitlab") + secrets, err := k8s.GitlabSecretClient.List(context.TODO(), metaV1.ListOptions{}) + + var gitlabRootPasswordSecretName string + + for _, secret := range secrets.Items { + if strings.Contains(secret.Name, "initial-root-password") { + gitlabRootPasswordSecretName = secret.Name + log.Println("gitlab initial root password secret name: ", gitlabRootPasswordSecretName) + } + } + gitlabRootPassword := k8s.GetSecretValue(k8s.GitlabSecretClient, gitlabRootPasswordSecretName, "password") + + viper.Set("gitlab.podname", gitlabPodName) + viper.Set("gitlab.root.password", gitlabRootPassword) + viper.WriteConfig() + + gitlabToken := viper.GetString("gitlab.token") + + if gitlabToken == "" { + + log.Println("generating gitlab personal access token") + GitlabGeneratePersonalAccessToken(gitlabPodName) + + } + + gitlabRunnerToken := viper.GetString("gitlab.runnertoken") + + if gitlabRunnerToken == "" { + + log.Println("getting gitlab runner token") + gitlabRunnerRegistrationToken := k8s.GetSecretValue(k8s.GitlabSecretClient, "gitlab-gitlab-runner-secret", "runner-registration-token") + viper.Set("gitlab.runnertoken", gitlabRunnerRegistrationToken) + viper.WriteConfig() + } + +} + +func ApplyGitlabTerraform(directory string) { + config := configs.ReadConfig() + if !viper.GetBool("create.terraformapplied.gitlab") { + log.Println("Executing ApplyGitlabTerraform") + if config.DryRun { + log.Printf("[#99] Dry-run mode, ApplyGitlabTerraform skipped.") + return + } + // Prepare for terraform gitlab execution + os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) + os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") + + directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) + err := os.Chdir(directory) + if err != nil { + log.Panic("error: could not change directory to " + directory) + } + _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") + if errInit != nil { + panic(fmt.Sprintf("error: terraform init for gitlab failed %s", err)) + } + _, _, errApply := pkg.ExecShellReturnStrings(config.TerraformPath, "apply", "-auto-approve") + if errApply != nil { + panic(fmt.Sprintf("error: terraform apply for gitlab failed %s", err)) + } + os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) + viper.Set("create.terraformapplied.gitlab", true) + viper.WriteConfig() + } else { + log.Println("Skipping: ApplyGitlabTerraform") + } +} + +func GitlabKeyUpload() { + config := configs.ReadConfig() + // upload ssh public key + if !viper.GetBool("gitlab.keyuploaded") { + log.Println("Executing GitlabKeyUpload") + log.Println("uploading ssh public key for gitlab user") + if config.DryRun { + log.Printf("[#99] Dry-run mode, GitlabKeyUpload skipped.") + return + } + log.Println("uploading ssh public key to gitlab") + gitlabToken := viper.GetString("gitlab.token") + data := url.Values{ + "title": {"kubefirst"}, + "key": {viper.GetString("botpublickey")}, + } + + gitlabUrlBase := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) + + resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) + if err != nil { + log.Fatal(err) + } + var res map[string]interface{} + json.NewDecoder(resp.Body).Decode(&res) + log.Println(res) + log.Println("ssh public key uploaded to gitlab") + viper.Set("gitlab.keyuploaded", true) + viper.WriteConfig() + } else { + log.Println("Skipping: GitlabKeyUpload") + log.Println("ssh public key already uploaded to gitlab") + } +} + +func DestroyGitlabTerraform() { + config := configs.ReadConfig() + log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") + // kubeconfig := os.Getenv("HOME") + "/.kube/config" + // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) + // argocdclientset, err := argocdclientset.NewForConfig(config) + // if err != nil { + // return nil, err + // } + + //* should we git clone the gitops repo when destroy is run back to their + //* local host to get the latest values of gitops + + os.Setenv("AWS_REGION", viper.GetString("aws.region")) + os.Setenv("AWS_ACCOUNT_ID", viper.GetString("aws.accountid")) + os.Setenv("HOSTED_ZONE_NAME", viper.GetString("aws.hostedzonename")) + os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) + + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) + + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) + err := os.Chdir(directory) + if err != nil { + log.Panicf("error: could not change directory to " + directory) + } + + os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") + + if !config.SkipGitlabTerraform { + tfInitGitlabCmd := exec.Command(config.TerraformPath, "init") + tfInitGitlabCmd.Stdout = os.Stdout + tfInitGitlabCmd.Stderr = os.Stderr + err = tfInitGitlabCmd.Run() + if err != nil { + log.Panicf("failed to terraform init gitlab %s", err) + } + + tfDestroyGitlabCmd := exec.Command(config.TerraformPath, "destroy", "-auto-approve") + tfDestroyGitlabCmd.Stdout = os.Stdout + tfDestroyGitlabCmd.Stderr = os.Stderr + err = tfDestroyGitlabCmd.Run() + if err != nil { + log.Panicf("failed to terraform destroy gitlab %s", err) + } + + viper.Set("destroy.terraformdestroy.gitlab", true) + viper.WriteConfig() + } else { + log.Println("skip: DestroyGitlabTerraform") + } +} diff --git a/internal/helm/helm.go b/internal/helm/helm.go new file mode 100644 index 000000000..95ba8246d --- /dev/null +++ b/internal/helm/helm.go @@ -0,0 +1,50 @@ +package helm + +import ( + "fmt" + "github.com/kubefirst/nebulous/configs" + "github.com/spf13/viper" + "log" + "os" + "os/exec" +) + +func InstallArgocd(home string) { + config := configs.ReadConfig() + if !viper.GetBool("create.argocd.helm") { + if config.DryRun { + log.Printf("[#99] Dry-run mode, helmInstallArgocd skipped.") + return + } + // ! commenting out until a clean execution is necessary // create namespace + helmRepoAddArgocd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") + helmRepoAddArgocd.Stdout = os.Stdout + helmRepoAddArgocd.Stderr = os.Stderr + err := helmRepoAddArgocd.Run() + if err != nil { + log.Panicf("error: could not run helm repo add %s", err) + } + + helmRepoUpdate := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "update") + helmRepoUpdate.Stdout = os.Stdout + helmRepoUpdate.Stderr = os.Stderr + err = helmRepoUpdate.Run() + if err != nil { + log.Panicf("error: could not helm repo update %s", err) + } + + helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") + helmInstallArgocdCmd.Stdout = os.Stdout + helmInstallArgocdCmd.Stderr = os.Stderr + err = helmInstallArgocdCmd.Run() + if err != nil { + log.Panicf("error: could not helm install argocd command %s", err) + } + + viper.Set("create.argocd.helm", true) + err = viper.WriteConfig() + if err != nil { + log.Panicf("error: could not write to viper config") + } + } +} diff --git a/pkg/helpers.go b/pkg/helpers.go new file mode 100644 index 000000000..f1349f1f4 --- /dev/null +++ b/pkg/helpers.go @@ -0,0 +1,214 @@ +package pkg + +import ( + "bytes" + "context" + b64 "encoding/base64" + "fmt" + "github.com/ghodss/yaml" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/k8s" + "github.com/spf13/viper" + "html/template" + "io/ioutil" + v1 "k8s.io/api/core/v1" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "log" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func Detokenize(path string) { + + err := filepath.Walk(path, DetokenizeDirectory) + if err != nil { + panic(err) + } +} + +func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if fi.IsDir() { + return nil // + } + + if strings.Contains(path, ".gitClient") || strings.Contains(path, ".terraform") { + return nil + } + + matched, err := filepath.Match("*", fi.Name()) + + if err != nil { + panic(err) + } + + if matched { + read, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + // todo should Detokenize be a switch statement based on a value found in viper? + gitlabConfigured := viper.GetBool("gitlab.keyuploaded") + + newContents := "" + + if gitlabConfigured { + newContents = strings.Replace(string(read), "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")), -1) + } else { + newContents = strings.Replace(string(read), "https://gitlab./kubefirst/gitops.git", "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", -1) + } + + botPublicKey := viper.GetString("botpublickey") + domainId := viper.GetString("aws.domainid") + hostedzonename := viper.GetString("aws.hostedzonename") + bucketStateStore := viper.GetString("bucket.state-store.name") + bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") + bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") + bucketChartmuseum := viper.GetString("bucket.chartmuseum.name") + region := viper.GetString("aws.region") + adminEmail := viper.GetString("adminemail") + awsAccountId := viper.GetString("aws.accountid") + kmsKeyId := viper.GetString("vault.kmskeyid") + + newContents = strings.Replace(newContents, "", strings.TrimSpace(botPublicKey), -1) + newContents = strings.Replace(newContents, "", bucketStateStore, -1) + newContents = strings.Replace(newContents, "", bucketArgoArtifacts, -1) + newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) + newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) + newContents = strings.Replace(newContents, "", domainId, -1) + newContents = strings.Replace(newContents, "", hostedzonename, -1) + newContents = strings.Replace(newContents, "", region, -1) + newContents = strings.Replace(newContents, "", adminEmail, -1) + newContents = strings.Replace(newContents, "", awsAccountId, -1) + if kmsKeyId != "" { + newContents = strings.Replace(newContents, "", kmsKeyId, -1) + } + + if viper.GetBool("create.terraformapplied.gitlab") { + newContents = strings.Replace(newContents, "", hostedzonename, -1) + newContents = strings.Replace(newContents, "", region, -1) + newContents = strings.Replace(newContents, "", awsAccountId, -1) + } + + err = ioutil.WriteFile(path, []byte(newContents), 0) + if err != nil { + panic(err) + } + + } + + return nil +} + +func ChangeRegistryToGitLab() { + config := configs.ReadConfig() + if !viper.GetBool("gitlab.registry") { + if config.DryRun { + log.Printf("[#99] Dry-run mode, ChangeRegistryToGitLab skipped.") + return + } + + type ArgocdGitCreds struct { + PersonalAccessToken string + URL string + FullURL string + } + + pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) + url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.hostedzonename")))) + fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")))) + + creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} + + var argocdRepositoryAccessTokenSecret *v1.Secret + k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) + if err != nil { + log.Panicf("error getting client from kubeconfig") + } + clientset, err := kubernetes.NewForConfig(k8sConfig) + if err != nil { + log.Panicf("error getting kubeconfig for clientset") + } + k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") + + var secrets bytes.Buffer + + c, err := template.New("creds-gitlab").Parse(` + apiVersion: v1 + data: + password: {{ .PersonalAccessToken }} + url: {{ .URL }} + username: cm9vdA== + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repo-creds + name: creds-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&secrets, creds); err != nil { + log.Panicf("error executing golang template for git repository credentials template %s", err) + } + + ba := []byte(secrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + + _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + log.Panicf("error creating argocd repository credentials template secret %s", err) + } + + var repoSecrets bytes.Buffer + + c, err = template.New("repo-gitlab").Parse(` + apiVersion: v1 + data: + project: ZGVmYXVsdA== + type: Z2l0 + url: {{ .FullURL }} + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repository + name: repo-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&repoSecrets, creds); err != nil { + log.Panicf("error executing golang template for gitops repository template %s", err) + } + + ba = []byte(repoSecrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + + _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + log.Panicf("error creating argocd repository connection secret %s", err) + } + + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.HomePath)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) + } + + viper.Set("gitlab.registry", true) + viper.WriteConfig() + } else { + log.Println("Skipping: ChangeRegistryToGitLab") + } +} From 1576b1ecd878092d613f350bd30fef35478930e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 18:29:34 -0300 Subject: [PATCH 048/107] refactor: move ssh and gitlab functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/clean.go | 2 +- cmd/create.go | 2 +- internal/gitlab/gitlab.go | 111 +++++++++++++++++++++++++++++++++++ pkg/helpers.go | 119 -------------------------------------- pkg/keys.go | 41 +++++++++++-- 5 files changed, 148 insertions(+), 127 deletions(-) diff --git a/cmd/clean.go b/cmd/clean.go index 4faf749a0..03d84008b 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -33,5 +33,5 @@ to quickly create a Cobra application.`, } func init() { - nebulousCmd.AddCommand(cleanCmd) + initCmd.AddCommand(cleanCmd) } diff --git a/cmd/create.go b/cmd/create.go index faec27ea6..3e8c04270 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -86,7 +86,7 @@ to quickly create a Cobra application.`, gitlab.PushGitOpsToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) - pkg.ChangeRegistryToGitLab() + gitlab.ChangeRegistryToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) hydrateGitlabMetaphorRepo() diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index eeacb0588..3e90dce86 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -1,13 +1,16 @@ package gitlab import ( + "bytes" "context" "crypto/rand" "crypto/rsa" "crypto/x509" + b64 "encoding/base64" "encoding/json" "encoding/pem" "fmt" + "github.com/ghodss/yaml" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" @@ -17,6 +20,8 @@ import ( "github.com/kubefirst/nebulous/internal/k8s" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" + "html/template" + v1 "k8s.io/api/core/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" @@ -366,3 +371,109 @@ func DestroyGitlabTerraform() { log.Println("skip: DestroyGitlabTerraform") } } + +func ChangeRegistryToGitLab() { + config := configs.ReadConfig() + if !viper.GetBool("gitlab.registry") { + if config.DryRun { + log.Printf("[#99] Dry-run mode, ChangeRegistryToGitLab skipped.") + return + } + + type ArgocdGitCreds struct { + PersonalAccessToken string + URL string + FullURL string + } + + pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) + url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.hostedzonename")))) + fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")))) + + creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} + + var argocdRepositoryAccessTokenSecret *v1.Secret + k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) + if err != nil { + log.Panicf("error getting client from kubeconfig") + } + clientset, err := kubernetes.NewForConfig(k8sConfig) + if err != nil { + log.Panicf("error getting kubeconfig for clientset") + } + k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") + + var secrets bytes.Buffer + + c, err := template.New("creds-gitlab").Parse(` + apiVersion: v1 + data: + password: {{ .PersonalAccessToken }} + url: {{ .URL }} + username: cm9vdA== + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repo-creds + name: creds-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&secrets, creds); err != nil { + log.Panicf("error executing golang template for git repository credentials template %s", err) + } + + ba := []byte(secrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + + _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + log.Panicf("error creating argocd repository credentials template secret %s", err) + } + + var repoSecrets bytes.Buffer + + c, err = template.New("repo-gitlab").Parse(` + apiVersion: v1 + data: + project: ZGVmYXVsdA== + type: Z2l0 + url: {{ .FullURL }} + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repository + name: repo-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&repoSecrets, creds); err != nil { + log.Panicf("error executing golang template for gitops repository template %s", err) + } + + ba = []byte(repoSecrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + + _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + log.Panicf("error creating argocd repository connection secret %s", err) + } + + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.HomePath)) + k.Stdout = os.Stdout + k.Stderr = os.Stderr + err = k.Run() + if err != nil { + log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) + } + + viper.Set("gitlab.registry", true) + viper.WriteConfig() + } else { + log.Println("Skipping: ChangeRegistryToGitLab") + } +} diff --git a/pkg/helpers.go b/pkg/helpers.go index f1349f1f4..0f8b695d0 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -1,23 +1,10 @@ package pkg import ( - "bytes" - "context" - b64 "encoding/base64" "fmt" - "github.com/ghodss/yaml" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/k8s" "github.com/spf13/viper" - "html/template" "io/ioutil" - v1 "k8s.io/api/core/v1" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "log" "os" - "os/exec" "path/filepath" "strings" ) @@ -106,109 +93,3 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { return nil } - -func ChangeRegistryToGitLab() { - config := configs.ReadConfig() - if !viper.GetBool("gitlab.registry") { - if config.DryRun { - log.Printf("[#99] Dry-run mode, ChangeRegistryToGitLab skipped.") - return - } - - type ArgocdGitCreds struct { - PersonalAccessToken string - URL string - FullURL string - } - - pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) - url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.hostedzonename")))) - fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")))) - - creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} - - var argocdRepositoryAccessTokenSecret *v1.Secret - k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) - if err != nil { - log.Panicf("error getting client from kubeconfig") - } - clientset, err := kubernetes.NewForConfig(k8sConfig) - if err != nil { - log.Panicf("error getting kubeconfig for clientset") - } - k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") - - var secrets bytes.Buffer - - c, err := template.New("creds-gitlab").Parse(` - apiVersion: v1 - data: - password: {{ .PersonalAccessToken }} - url: {{ .URL }} - username: cm9vdA== - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repo-creds - name: creds-gitlab - namespace: argocd - type: Opaque - `) - if err := c.Execute(&secrets, creds); err != nil { - log.Panicf("error executing golang template for git repository credentials template %s", err) - } - - ba := []byte(secrets.String()) - err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - - _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) - if err != nil { - log.Panicf("error creating argocd repository credentials template secret %s", err) - } - - var repoSecrets bytes.Buffer - - c, err = template.New("repo-gitlab").Parse(` - apiVersion: v1 - data: - project: ZGVmYXVsdA== - type: Z2l0 - url: {{ .FullURL }} - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repository - name: repo-gitlab - namespace: argocd - type: Opaque - `) - if err := c.Execute(&repoSecrets, creds); err != nil { - log.Panicf("error executing golang template for gitops repository template %s", err) - } - - ba = []byte(repoSecrets.String()) - err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - - _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) - if err != nil { - log.Panicf("error creating argocd repository connection secret %s", err) - } - - k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.HomePath)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err = k.Run() - if err != nil { - log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) - } - - viper.Set("gitlab.registry", true) - viper.WriteConfig() - } else { - log.Println("Skipping: ChangeRegistryToGitLab") - } -} diff --git a/pkg/keys.go b/pkg/keys.go index ac4043e8a..8011c497c 100644 --- a/pkg/keys.go +++ b/pkg/keys.go @@ -1,11 +1,15 @@ package pkg import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" "fmt" - "github.com/go-git/go-git/v5/plumbing/transport/ssh" + goGitSsh "github.com/go-git/go-git/v5/plumbing/transport/ssh" "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/gitlab" "github.com/spf13/viper" + "golang.org/x/crypto/ssh" "io/ioutil" "log" "strings" @@ -16,7 +20,7 @@ func CreateSshKeyPair() { publicKey := viper.GetString("botpublickey") if publicKey == "" { log.Println("generating new key pair") - publicKey, privateKey, _ := gitlab.GenerateKey() + publicKey, privateKey, _ := GenerateKey() viper.Set("botPublicKey", publicKey) viper.Set("botPrivateKey", privateKey) err := viper.WriteConfig() @@ -70,11 +74,36 @@ configs: } } -func PublicKey() (*ssh.PublicKeys, error) { - var publicKey *ssh.PublicKeys - publicKey, err := ssh.NewPublicKeys("gitClient", []byte(viper.GetString("botprivatekey")), "") +func PublicKey() (*goGitSsh.PublicKeys, error) { + var publicKey *goGitSsh.PublicKeys + publicKey, err := goGitSsh.NewPublicKeys("gitClient", []byte(viper.GetString("botprivatekey")), "") if err != nil { return nil, err } return publicKey, err } + +// GenerateKey generate public and private keys to be consumed by GitLab. +func GenerateKey() (string, string, error) { + reader := rand.Reader + bitSize := 2048 + + key, err := rsa.GenerateKey(reader, bitSize) + if err != nil { + return "", "", err + } + + pub, err := ssh.NewPublicKey(key.Public()) + if err != nil { + return "", "", err + } + publicKey := string(ssh.MarshalAuthorizedKey(pub)) + // encode RSA key + privateKey := string(pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), + }, + )) + + return publicKey, privateKey, nil +} From f5b2625bd4adb6cac0381c0fe07aeb476640f5d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 18:51:49 -0300 Subject: [PATCH 049/107] refactor: move vault and softserve to internal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 16 +- cmd/destroy.go | 3 +- cmd/stepsMetaphor.go | 78 ------- cmd/stepsVault.go | 198 ------------------ cmd/version.go | 1 - internal/gitlab/gitlab.go | 64 ++++++ .../softserve/softserve.go | 18 +- .../terraform/terraform.go | 6 +- internal/vault/vault.go | 192 +++++++++++++++++ 9 files changed, 280 insertions(+), 296 deletions(-) delete mode 100644 cmd/stepsMetaphor.go delete mode 100644 cmd/stepsVault.go rename cmd/stepsSoftServe.go => internal/softserve/softserve.go (89%) rename cmd/stepsBaseInstall.go => internal/terraform/terraform.go (96%) diff --git a/cmd/create.go b/cmd/create.go index 3e8c04270..cd0596a56 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -6,7 +6,10 @@ import ( "github.com/kubefirst/nebulous/internal/argocd" "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/helm" + "github.com/kubefirst/nebulous/internal/softserve" "github.com/kubefirst/nebulous/internal/telemetry" + "github.com/kubefirst/nebulous/internal/terraform" + "github.com/kubefirst/nebulous/internal/vault" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -56,11 +59,11 @@ to quickly create a Cobra application.`, } directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) - applyBaseTerraform(cmd, directory) + terraform.ApplyBaseTerraform(cmd, directory) Trackers[trackerStage20].Tracker.Increment(int64(1)) - createSoftServe(config.KubeConfigPath) + softserve.CreateSoftServe(config.KubeConfigPath) Trackers[trackerStage21].Tracker.Increment(int64(1)) - configureSoftserveAndPush() + softserve.ConfigureSoftServeAndPush() Trackers[trackerStage21].Tracker.Increment(int64(1)) helm.InstallArgocd(config.HomePath) Trackers[trackerStage22].Tracker.Increment(int64(1)) @@ -77,9 +80,9 @@ to quickly create a Cobra application.`, Trackers[trackerStage22].Tracker.Increment(int64(1)) if !skipVault { - configureVault() + vault.ConfigureVault() Trackers[trackerStage23].Tracker.Increment(int64(1)) - addGitlabOidcApplications() + vault.AddGitlabOidcApplications() Trackers[trackerStage23].Tracker.Increment(int64(1)) gitlab.AwaitGitlab() Trackers[trackerStage22].Tracker.Increment(int64(1)) @@ -89,7 +92,8 @@ to quickly create a Cobra application.`, gitlab.ChangeRegistryToGitLab() Trackers[trackerStage22].Tracker.Increment(int64(1)) - hydrateGitlabMetaphorRepo() + gitlab.HydrateGitlabMetaphorRepo() + Trackers[trackerStage23].Tracker.Increment(int64(1)) token := argocd.GetArgocdAuthToken() diff --git a/cmd/destroy.go b/cmd/destroy.go index 90077281a..c4a544e1b 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -5,6 +5,7 @@ import ( "github.com/kubefirst/nebulous/internal/aws" "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/k8s" + "github.com/kubefirst/nebulous/internal/terraform" "github.com/spf13/cobra" "log" "os" @@ -37,7 +38,7 @@ if the registry has already been delteted.`, gitlab.DestroyGitlabTerraform() // delete argocd registry k8s.DeleteRegistryApplication() - destroyBaseTerraform() + terraform.DestroyBaseTerraform() //TODO: Remove buckets? Opt-in flag aws.DestroyBucketsInUse() }, diff --git a/cmd/stepsMetaphor.go b/cmd/stepsMetaphor.go deleted file mode 100644 index 7697fc0ab..000000000 --- a/cmd/stepsMetaphor.go +++ /dev/null @@ -1,78 +0,0 @@ -package cmd - -import ( - "fmt" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing/object" - gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/pkg" - "github.com/spf13/viper" - "log" - "time" -) - -func hydrateGitlabMetaphorRepo() { - cfg := configs.ReadConfig() - //TODO: Should this be skipped if already executed? - if !viper.GetBool("create.gitlabmetaphor.cloned") { - if cfg.DryRun { - log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") - return - } - - metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", cfg.HomePath) - - url := "https://github.com/kubefirst/metaphor-template" - - metaphorTemplateRepo, err := git.PlainClone(metaphorTemplateDir, false, &git.CloneOptions{ - URL: url, - }) - if err != nil { - log.Panicf("error cloning metaphor-template repo") - } - viper.Set("create.gitlabmetaphor.cloned", true) - - pkg.Detokenize(metaphorTemplateDir) - - viper.Set("create.gitlabmetaphor.detokenized", true) - - // todo make global - gitlabURL := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.hostedzonename")) - log.Println("gitClient remote add origin", gitlabURL) - _, err = metaphorTemplateRepo.CreateRemote(&config.RemoteConfig{ - Name: "gitlab", - URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.gitClient", gitlabURL)}, - }) - - w, _ := metaphorTemplateRepo.Worktree() - - log.Println("Committing detokenized metaphor content") - w.Add(".") - w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ - Author: &object.Signature{ - Name: "kubefirst-bot", - Email: "kubefirst-bot@kubefirst.com", - When: time.Now(), - }, - }) - - err = metaphorTemplateRepo.Push(&git.PushOptions{ - RemoteName: "gitlab", - Auth: &gitHttp.BasicAuth{ - Username: "root", - Password: viper.GetString("gitlab.token"), - }, - }) - if err != nil { - log.Panicf("error pushing detokenized metaphor repository to remote at" + gitlabURL) - } - - viper.Set("create.gitlabmetaphor.pushed", true) - viper.WriteConfig() - } else { - log.Println("Skipping: hydrateGitlabMetaphorRepo") - } - -} diff --git a/cmd/stepsVault.go b/cmd/stepsVault.go deleted file mode 100644 index 67c02f91b..000000000 --- a/cmd/stepsVault.go +++ /dev/null @@ -1,198 +0,0 @@ -package cmd - -import ( - "fmt" - vault "github.com/hashicorp/vault/api" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/k8s" - internalVault "github.com/kubefirst/nebulous/internal/vault" - "github.com/spf13/viper" - gitlab "github.com/xanzy/go-gitlab" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "log" - "os" - "os/exec" - "syscall" -) - -func configureVault() { - config := configs.ReadConfig() - if !viper.GetBool("create.terraformapplied.vault") { - if config.DryRun { - log.Printf("[#99] Dry-run mode, configureVault skipped.") - return - } - // ``` - // NOTE: the terraform here produces unnecessary $var.varname vars in the atlantis secret for nonsensitive values - // the following atlantis secrets shouldn't have vars in the gitops source code for the atlantis secret, they - // should look like us-east-1, in flat string code as non-sensitive vals - refactor soon. - // "TF_VAR_aws_region": "us-east-1", - // "TF_VAR_aws_account_id": "${var.aws_account_id}", - // "TF_VAR_email_address": "${var.email_address}", - // "TF_VAR_hosted_zone_id": "${var.hosted_zone_id}", - // "TF_VAR_hosted_zone_name": "${var.hosted_zone_name}", - // "TF_VAR_vault_addr": "${var.vault_addr}", - // ``` - // ... obviously keep the sensitive values bound to vars - - k8sClient, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) - if err != nil { - log.Panicf("error: getting k8sClient %s", err) - } - clientset, err := kubernetes.NewForConfig(k8sClient) - if err != nil { - log.Panicf("error: getting k8sClient &s", err) - } - - k8s.VaultSecretClient = clientset.CoreV1().Secrets("vault") - vaultToken, err := internalVault.GetVaultRootToken(k8s.VaultSecretClient) - if err != nil { - log.Panicf("unable to get vault root token, error: %s", err) - } - - viper.Set("vault.token", vaultToken) - viper.WriteConfig() - - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err = kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to vault namespce svc/vault %s", err) - } - - // Prepare for terraform vault execution - os.Setenv("VAULT_ADDR", "http://localhost:8200") - os.Setenv("VAULT_TOKEN", vaultToken) - - os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename"))) - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_email_address", viper.GetString("adminemail")) - os.Setenv("TF_VAR_gitlab_runner_token", viper.GetString("gitlab.runnertoken")) - os.Setenv("TF_VAR_gitlab_token", viper.GetString("gitlab.token")) - os.Setenv("TF_VAR_hosted_zone_id", viper.GetString("aws.domainid")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - os.Setenv("TF_VAR_vault_token", viper.GetString("aws.hostedzonename")) - os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") - - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", config.HomePath) - err = os.Chdir(directory) - if err != nil { - log.Panicf("error: could not change directory to " + directory) - } - - tfInitCmd := exec.Command(config.TerraformPath, "init") - tfInitCmd.Stdout = os.Stdout - tfInitCmd.Stderr = os.Stderr - err = tfInitCmd.Run() - if err != nil { - log.Panicf("error: terraform init failed %s", err) - } - - tfApplyCmd := exec.Command(config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") - tfApplyCmd.Stdout = os.Stdout - tfApplyCmd.Stderr = os.Stderr - err = tfApplyCmd.Run() - if err != nil { - log.Panicf("error: terraform apply failed %s", err) - } - - viper.Set("create.terraformapplied.vault", true) - viper.WriteConfig() - } else { - log.Println("Skipping: configureVault") - } -} - -func addGitlabOidcApplications() { - config := configs.ReadConfig() - //TODO: Should this skipped if already executed. - if config.DryRun { - log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") - return - } - domain := viper.GetString("aws.hostedzonename") - git, err := gitlab.NewClient( - viper.GetString("gitlab.token"), - gitlab.WithBaseURL(fmt.Sprintf("https://gitlab.%s/api/v4", domain)), - ) - if err != nil { - log.Fatal(err) - } - - apps := []string{"argo", "argocd", "vault"} - cb := make(map[string]string) - cb["argo"] = fmt.Sprintf("https://argo.%s/oauth2/callback", domain) - cb["argocd"] = fmt.Sprintf("https://argocd.%s/auth/callback", domain) - cb["vault"] = fmt.Sprintf("https://vault.%s:8250/oidc/callback http://localhost:8250/oidc/callback https://vault.%s/ui/vault/auth/oidc/oidc/callback http://localhost:8200/ui/vault/auth/oidc/oidc/callback", domain, domain) - - for _, app := range apps { - log.Println("checking to see if", app, "oidc application needs to be created in gitlab") - appId := viper.GetString(fmt.Sprintf("gitlab.oidc.%s.applicationid", app)) - if appId == "" { - - // Create an application - opts := &gitlab.CreateApplicationOptions{ - Name: gitlab.String(app), - RedirectURI: gitlab.String(cb[app]), - Scopes: gitlab.String("read_user openid email"), - } - createdApp, _, err := git.Applications.CreateApplication(opts) - if err != nil { - log.Fatal(err) - } - - // List all applications - existingApps, _, err := git.Applications.ListApplications(&gitlab.ListApplicationsOptions{}) - if err != nil { - log.Panicf("error: could not list applications from gitlab") - } - - created := false - for _, existingApp := range existingApps { - if existingApp.ApplicationName == app { - created = true - } - } - if created { - log.Println("created gitlab oidc application with applicationid", createdApp.ApplicationID) - viper.Set(fmt.Sprintf("gitlab.oidc.%s.applicationid", app), createdApp.ApplicationID) - viper.Set(fmt.Sprintf("gitlab.oidc.%s.secret", app), createdApp.Secret) - - secretData := map[string]interface{}{ - "data": map[string]interface{}{ - "application_id": createdApp.ApplicationID, - "secret": createdApp.Secret, - }, - } - secretPath := fmt.Sprintf("secret/data/oidc/%s", app) - addVaultSecret(secretPath, secretData) - viper.WriteConfig() - } else { - log.Panicf("could not create gitlab oidc application %s", app) - } - } - } -} - -func addVaultSecret(secretPath string, secretData map[string]interface{}) { - config := vault.DefaultConfig() - config.Address = fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename")) - - client, err := vault.NewClient(config) - if err != nil { - log.Panicf("unable to initialize vault client %s", err) - } - - client.SetToken(viper.GetString("vault.token")) - - _, err = client.Logical().Write(secretPath, secretData) - if err != nil { - log.Panicf("unable to write secret vault secret %s - error: %s", secretPath, err) - } else { - log.Println("secret successfully written to path: ", secretPath) - } -} diff --git a/cmd/version.go b/cmd/version.go index 01644bd04..d57076b36 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -17,6 +17,5 @@ var versionCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { config := configs.ReadConfig() log.Printf("flare-cli golang utility version: v%s", config.KubefirstVersion) - }, } diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 3e90dce86..378e97a13 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -477,3 +477,67 @@ func ChangeRegistryToGitLab() { log.Println("Skipping: ChangeRegistryToGitLab") } } + +func HydrateGitlabMetaphorRepo() { + cfg := configs.ReadConfig() + //TODO: Should this be skipped if already executed? + if !viper.GetBool("create.gitlabmetaphor.cloned") { + if cfg.DryRun { + log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") + return + } + + metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", cfg.HomePath) + + url := "https://github.com/kubefirst/metaphor-template" + + metaphorTemplateRepo, err := git.PlainClone(metaphorTemplateDir, false, &git.CloneOptions{ + URL: url, + }) + if err != nil { + log.Panicf("error cloning metaphor-template repo") + } + viper.Set("create.gitlabmetaphor.cloned", true) + + pkg.Detokenize(metaphorTemplateDir) + + viper.Set("create.gitlabmetaphor.detokenized", true) + + // todo make global + gitlabURL := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.hostedzonename")) + log.Println("gitClient remote add origin", gitlabURL) + _, err = metaphorTemplateRepo.CreateRemote(&config.RemoteConfig{ + Name: "gitlab", + URLs: []string{fmt.Sprintf("%s/kubefirst/metaphor.gitClient", gitlabURL)}, + }) + + w, _ := metaphorTemplateRepo.Worktree() + + log.Println("Committing detokenized metaphor content") + w.Add(".") + w.Commit("setting new remote upstream to gitlab", &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: "kubefirst-bot@kubefirst.com", + When: time.Now(), + }, + }) + + err = metaphorTemplateRepo.Push(&git.PushOptions{ + RemoteName: "gitlab", + Auth: &gitHttp.BasicAuth{ + Username: "root", + Password: viper.GetString("gitlab.token"), + }, + }) + if err != nil { + log.Panicf("error pushing detokenized metaphor repository to remote at" + gitlabURL) + } + + viper.Set("create.gitlabmetaphor.pushed", true) + viper.WriteConfig() + } else { + log.Println("Skipping: hydrateGitlabMetaphorRepo") + } + +} diff --git a/cmd/stepsSoftServe.go b/internal/softserve/softserve.go similarity index 89% rename from cmd/stepsSoftServe.go rename to internal/softserve/softserve.go index 1db047045..cdc55821d 100644 --- a/cmd/stepsSoftServe.go +++ b/internal/softserve/softserve.go @@ -1,4 +1,4 @@ -package cmd +package softserve import ( "fmt" @@ -18,12 +18,12 @@ import ( "time" ) -func createSoftServe(kubeconfigPath string) { +func CreateSoftServe(kubeconfigPath string) { config := configs.ReadConfig() if !viper.GetBool("create.softserve.create") { - log.Println("Executing createSoftServe") + log.Println("Executing CreateSoftServe") if config.DryRun { - log.Printf("[#99] Dry-run mode, createSoftServe skipped.") + log.Printf("[#99] Dry-run mode, CreateSoftServe skipped.") return } toolsDir := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) @@ -47,18 +47,18 @@ func createSoftServe(kubeconfigPath string) { time.Sleep(60 * time.Second) //TODO: Update mechanism of waiting } else { - log.Println("Skipping: createSoftServe") + log.Println("Skipping: CreateSoftServe") } } -func configureSoftserveAndPush() { +func ConfigureSoftServeAndPush() { config := configs.ReadConfig() configureAndPushFlag := viper.GetBool("create.softserve.configure") if configureAndPushFlag != true { - log.Println("Executing configureSoftserveAndPush") + log.Println("Executing ConfigureSoftServeAndPush") if config.DryRun { - log.Printf("[#99] Dry-run mode, configureSoftserveAndPush skipped.") + log.Printf("[#99] Dry-run mode, ConfigureSoftServeAndPush skipped.") return } kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") @@ -77,7 +77,7 @@ func configureSoftserveAndPush() { viper.WriteConfig() time.Sleep(30 * time.Second) } else { - log.Println("Skipping: configureSoftserveAndPush") + log.Println("Skipping: ConfigureSoftServeAndPush") } } diff --git a/cmd/stepsBaseInstall.go b/internal/terraform/terraform.go similarity index 96% rename from cmd/stepsBaseInstall.go rename to internal/terraform/terraform.go index 6c7cbc6c8..6e244f538 100644 --- a/cmd/stepsBaseInstall.go +++ b/internal/terraform/terraform.go @@ -1,4 +1,4 @@ -package cmd +package terraform import ( "fmt" @@ -11,7 +11,7 @@ import ( "strings" ) -func applyBaseTerraform(cmd *cobra.Command, directory string) { +func ApplyBaseTerraform(cmd *cobra.Command, directory string) { config := configs.ReadConfig() applyBase := viper.GetBool("create.terraformapplied.base") if applyBase != true { @@ -53,7 +53,7 @@ func applyBaseTerraform(cmd *cobra.Command, directory string) { } } -func destroyBaseTerraform() { +func DestroyBaseTerraform() { config := configs.ReadConfig() if !config.SkipBaseTerraform { directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 75ba6d0c2..daa21f006 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -3,9 +3,20 @@ package vault import ( "context" "encoding/json" + "fmt" + vault "github.com/hashicorp/vault/api" + "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/k8s" + "github.com/spf13/viper" + gitlab "github.com/xanzy/go-gitlab" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/clientcmd" "log" + "os" + "os/exec" + "syscall" ) // GetVaultRootToken get `vault-unseal-keys` token on Vault. @@ -27,3 +38,184 @@ func GetVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) (string, e } return vaultRootToken, nil } + +func ConfigureVault() { + config := configs.ReadConfig() + if !viper.GetBool("create.terraformapplied.vault") { + if config.DryRun { + log.Printf("[#99] Dry-run mode, configureVault skipped.") + return + } + // ``` + // NOTE: the terraform here produces unnecessary $var.varname vars in the atlantis secret for nonsensitive values + // the following atlantis secrets shouldn't have vars in the gitops source code for the atlantis secret, they + // should look like us-east-1, in flat string code as non-sensitive vals - refactor soon. + // "TF_VAR_aws_region": "us-east-1", + // "TF_VAR_aws_account_id": "${var.aws_account_id}", + // "TF_VAR_email_address": "${var.email_address}", + // "TF_VAR_hosted_zone_id": "${var.hosted_zone_id}", + // "TF_VAR_hosted_zone_name": "${var.hosted_zone_name}", + // "TF_VAR_vault_addr": "${var.vault_addr}", + // ``` + // ... obviously keep the sensitive values bound to vars + + k8sClient, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) + if err != nil { + log.Panicf("error: getting k8sClient %s", err) + } + clientset, err := kubernetes.NewForConfig(k8sClient) + if err != nil { + log.Panicf("error: getting k8sClient &s", err) + } + + k8s.VaultSecretClient = clientset.CoreV1().Secrets("vault") + vaultToken, err := GetVaultRootToken(k8s.VaultSecretClient) + if err != nil { + log.Panicf("unable to get vault root token, error: %s", err) + } + + viper.Set("vault.token", vaultToken) + viper.WriteConfig() + + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + err = kPortForward.Start() + defer kPortForward.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to vault namespce svc/vault %s", err) + } + + // Prepare for terraform vault execution + os.Setenv("VAULT_ADDR", "http://localhost:8200") + os.Setenv("VAULT_TOKEN", vaultToken) + + os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename"))) + os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) + os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) + os.Setenv("TF_VAR_email_address", viper.GetString("adminemail")) + os.Setenv("TF_VAR_gitlab_runner_token", viper.GetString("gitlab.runnertoken")) + os.Setenv("TF_VAR_gitlab_token", viper.GetString("gitlab.token")) + os.Setenv("TF_VAR_hosted_zone_id", viper.GetString("aws.domainid")) + os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) + os.Setenv("TF_VAR_vault_token", viper.GetString("aws.hostedzonename")) + os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") + + directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", config.HomePath) + err = os.Chdir(directory) + if err != nil { + log.Panicf("error: could not change directory to " + directory) + } + + tfInitCmd := exec.Command(config.TerraformPath, "init") + tfInitCmd.Stdout = os.Stdout + tfInitCmd.Stderr = os.Stderr + err = tfInitCmd.Run() + if err != nil { + log.Panicf("error: terraform init failed %s", err) + } + + tfApplyCmd := exec.Command(config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") + tfApplyCmd.Stdout = os.Stdout + tfApplyCmd.Stderr = os.Stderr + err = tfApplyCmd.Run() + if err != nil { + log.Panicf("error: terraform apply failed %s", err) + } + + viper.Set("create.terraformapplied.vault", true) + viper.WriteConfig() + } else { + log.Println("Skipping: configureVault") + } +} + +func AddGitlabOidcApplications() { + config := configs.ReadConfig() + //TODO: Should this skipped if already executed. + if config.DryRun { + log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") + return + } + domain := viper.GetString("aws.hostedzonename") + git, err := gitlab.NewClient( + viper.GetString("gitlab.token"), + gitlab.WithBaseURL(fmt.Sprintf("https://gitlab.%s/api/v4", domain)), + ) + if err != nil { + log.Fatal(err) + } + + apps := []string{"argo", "argocd", "vault"} + cb := make(map[string]string) + cb["argo"] = fmt.Sprintf("https://argo.%s/oauth2/callback", domain) + cb["argocd"] = fmt.Sprintf("https://argocd.%s/auth/callback", domain) + cb["vault"] = fmt.Sprintf("https://vault.%s:8250/oidc/callback http://localhost:8250/oidc/callback https://vault.%s/ui/vault/auth/oidc/oidc/callback http://localhost:8200/ui/vault/auth/oidc/oidc/callback", domain, domain) + + for _, app := range apps { + log.Println("checking to see if", app, "oidc application needs to be created in gitlab") + appId := viper.GetString(fmt.Sprintf("gitlab.oidc.%s.applicationid", app)) + if appId == "" { + + // Create an application + opts := &gitlab.CreateApplicationOptions{ + Name: gitlab.String(app), + RedirectURI: gitlab.String(cb[app]), + Scopes: gitlab.String("read_user openid email"), + } + createdApp, _, err := git.Applications.CreateApplication(opts) + if err != nil { + log.Fatal(err) + } + + // List all applications + existingApps, _, err := git.Applications.ListApplications(&gitlab.ListApplicationsOptions{}) + if err != nil { + log.Panicf("error: could not list applications from gitlab") + } + + created := false + for _, existingApp := range existingApps { + if existingApp.ApplicationName == app { + created = true + } + } + if created { + log.Println("created gitlab oidc application with applicationid", createdApp.ApplicationID) + viper.Set(fmt.Sprintf("gitlab.oidc.%s.applicationid", app), createdApp.ApplicationID) + viper.Set(fmt.Sprintf("gitlab.oidc.%s.secret", app), createdApp.Secret) + + secretData := map[string]interface{}{ + "data": map[string]interface{}{ + "application_id": createdApp.ApplicationID, + "secret": createdApp.Secret, + }, + } + secretPath := fmt.Sprintf("secret/data/oidc/%s", app) + addVaultSecret(secretPath, secretData) + viper.WriteConfig() + } else { + log.Panicf("could not create gitlab oidc application %s", app) + } + } + } +} + +func addVaultSecret(secretPath string, secretData map[string]interface{}) { + config := vault.DefaultConfig() + config.Address = fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename")) + + client, err := vault.NewClient(config) + if err != nil { + log.Panicf("unable to initialize vault client %s", err) + } + + client.SetToken(viper.GetString("vault.token")) + + _, err = client.Logical().Write(secretPath, secretData) + if err != nil { + log.Panicf("unable to write secret vault secret %s - error: %s", secretPath, err) + } else { + log.Println("secret successfully written to path: ", secretPath) + } +} From 0bfeb1e49c5a508a8fd6159c657ae5bf372963b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 7 Jul 2022 19:09:27 -0300 Subject: [PATCH 050/107] chore: update logs to use logs.panic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/init.go | 12 ++++++------ cmd/root.go | 2 +- configs/config.go | 4 ++-- internal/downloadManager/download.go | 10 +++++----- internal/gitlab/gitlab.go | 8 ++++---- internal/k8s/kubernetes.go | 4 ++-- internal/softserve/softserve.go | 2 +- internal/terraform/terraform.go | 4 ++-- pkg/helpers.go | 9 +++++---- 9 files changed, 28 insertions(+), 27 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 6697967fc..7b077de2b 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -32,7 +32,7 @@ to quickly create a Cobra application.`, var err error config.DryRun, err = cmd.Flags().GetBool("dry-run") if err != nil { - panic(err) + log.Panic(err) } log.Println("dry run enabled:", config.DryRun) @@ -113,7 +113,7 @@ to quickly create a Cobra application.`, log.Println("calling download()") err = downloadManager.DownloadTools(config, trackers) if err != nil { - panic(err) + log.Panic(err) } log.Println("download() complete") @@ -153,22 +153,22 @@ func init() { initCmd.Flags().String("hosted-zone-name", "", "the domain to provision the kubefirst platform in") err := initCmd.MarkFlagRequired("hosted-zone-name") if err != nil { - panic(err) + log.Panic(err) } initCmd.Flags().String("admin-email", "", "the email address for the administrator as well as for lets-encrypt certificate emails") err = initCmd.MarkFlagRequired("admin-email") if err != nil { - panic(err) + log.Panic(err) } initCmd.Flags().String("cloud", "", "the cloud to provision infrastructure in") err = initCmd.MarkFlagRequired("cloud") if err != nil { - panic(err) + log.Panic(err) } initCmd.Flags().String("region", "", "the region to provision the cloud resources in") err = initCmd.MarkFlagRequired("region") if err != nil { - panic(err) + log.Panic(err) } initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.flare") diff --git a/cmd/root.go b/cmd/root.go index 66004511d..f7de07662 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -62,7 +62,7 @@ func initConfig() { log.Printf("Config file not found, creating a blank one: %s \n", cfgFile) err := os.WriteFile(cfgFile, []byte("createdBy: installer\n\n"), 0700) if err != nil { - panic(err) + log.Panic(err) } } diff --git a/configs/config.go b/configs/config.go index 8b6be99a3..76225d3d9 100644 --- a/configs/config.go +++ b/configs/config.go @@ -42,13 +42,13 @@ func ReadConfig() *Config { if err := env.Parse(&config); err != nil { log.Println("something went wrong loading the environment variables") - panic(err) + log.Panic(err) } var err error config.HomePath, err = os.UserHomeDir() if err != nil { - panic(err) + log.Panic(err) } config.LocalOs = runtime.GOOS diff --git a/internal/downloadManager/download.go b/internal/downloadManager/download.go index 33bfef91a..2a6cb6732 100644 --- a/internal/downloadManager/download.go +++ b/internal/downloadManager/download.go @@ -260,7 +260,7 @@ func unzip(zipFilepath string, unzipDirectory string) { dst := unzipDirectory archive, err := zip.OpenReader(zipFilepath) if err != nil { - panic(err) + log.Panic(err) } defer archive.Close() @@ -279,21 +279,21 @@ func unzip(zipFilepath string, unzipDirectory string) { } if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - panic(err) + log.Panic(err) } dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { - panic(err) + log.Panic(err) } fileInArchive, err := f.Open() if err != nil { - panic(err) + log.Panic(err) } if _, err := io.Copy(dstFile, fileInArchive); err != nil { - panic(err) + log.Panic(err) } dstFile.Close() diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 378e97a13..4ef638d0c 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -187,11 +187,11 @@ func ProduceGitlabTokens() { config := configs.ReadConfig() k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) if err != nil { - panic(err.Error()) + log.Panic(err.Error()) } clientset, err := kubernetes.NewForConfig(k8sConfig) if err != nil { - panic(err.Error()) + log.Panic(err.Error()) } log.Println("discovering gitlab toolbox pod") if config.DryRun { @@ -269,11 +269,11 @@ func ApplyGitlabTerraform(directory string) { } _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") if errInit != nil { - panic(fmt.Sprintf("error: terraform init for gitlab failed %s", err)) + log.Panic(fmt.Sprintf("error: terraform init for gitlab failed %s", err)) } _, _, errApply := pkg.ExecShellReturnStrings(config.TerraformPath, "apply", "-auto-approve") if errApply != nil { - panic(fmt.Sprintf("error: terraform apply for gitlab failed %s", err)) + log.Panic(fmt.Sprintf("error: terraform apply for gitlab failed %s", err)) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) viper.Set("create.terraformapplied.gitlab", true) diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index fcfad866b..ab3049e10 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -44,14 +44,14 @@ func getVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { secret, err := vaultSecretClient.Get(context.TODO(), name, metaV1.GetOptions{}) if err != nil { - panic(err.Error()) + log.Panic(err.Error()) } var jsonData map[string]interface{} for _, value := range secret.Data { if err := json.Unmarshal(value, &jsonData); err != nil { - panic(err) + log.Panic(err) } vaultRootToken = jsonData["root_token"].(string) } diff --git a/internal/softserve/softserve.go b/internal/softserve/softserve.go index cdc55821d..ae80fde43 100644 --- a/internal/softserve/softserve.go +++ b/internal/softserve/softserve.go @@ -110,7 +110,7 @@ func configureSoftServe() { err = ioutil.WriteFile(fmt.Sprintf("%s/config.yaml", directory), []byte(newFile), 0) if err != nil { - panic(err) + log.Panic(err) } println("re-wrote config.yaml", config.HomePath, "/.kubefirst/config") diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index 6e244f538..b41a19cab 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -30,11 +30,11 @@ func ApplyBaseTerraform(cmd *cobra.Command, directory string) { } _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") if errInit != nil { - panic(fmt.Sprintf("error: terraform init failed %v", err)) + log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) } _, _, errApply := pkg.ExecShellReturnStrings(config.TerraformPath, "apply", "-auto-approve") if errApply != nil { - panic(fmt.Sprintf("error: terraform init failed %v", err)) + log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) } keyOut, _, errKey := pkg.ExecShellReturnStrings(config.TerraformPath, "output", "vault_unseal_kms_key") if errKey != nil { diff --git a/pkg/helpers.go b/pkg/helpers.go index 0f8b695d0..535f5539d 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/spf13/viper" "io/ioutil" + "log" "os" "path/filepath" "strings" @@ -13,7 +14,7 @@ func Detokenize(path string) { err := filepath.Walk(path, DetokenizeDirectory) if err != nil { - panic(err) + log.Panic(err) } } @@ -33,13 +34,13 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { matched, err := filepath.Match("*", fi.Name()) if err != nil { - panic(err) + log.Panic(err) } if matched { read, err := ioutil.ReadFile(path) if err != nil { - panic(err) + log.Panic(err) } // todo should Detokenize be a switch statement based on a value found in viper? gitlabConfigured := viper.GetBool("gitlab.keyuploaded") @@ -86,7 +87,7 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { err = ioutil.WriteFile(path, []byte(newContents), 0) if err != nil { - panic(err) + log.Panic(err) } } From 0c8ed06179467dadb7b6c25e0d2d12bbf9b70829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Fri, 8 Jul 2022 13:05:03 -0300 Subject: [PATCH 051/107] refactor: move command line flags to function callers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 67 +++++++++++++++++++-------------- cmd/destroy.go | 36 +++++++++++++----- cmd/init.go | 16 ++++---- configs/config.go | 29 +++++++------- internal/argocd/argocd.go | 9 +++-- internal/aws/aws.go | 17 ++++----- internal/gitlab/gitlab.go | 35 +++++++++-------- internal/helm/helm.go | 6 +-- internal/k8s/kubernetes.go | 4 +- internal/softserve/softserve.go | 8 ++-- internal/terraform/terraform.go | 9 ++--- internal/vault/vault.go | 10 ++--- 12 files changed, 132 insertions(+), 114 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index cd0596a56..2efbf09fc 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -22,9 +22,6 @@ const trackerStage21 = "1 - Temporary SCM Install" const trackerStage22 = "2 - Argo/Final SCM Install" const trackerStage23 = "3 - Final Setup" -var skipVault bool -var skipGitlab bool - // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", @@ -39,6 +36,19 @@ to quickly create a Cobra application.`, config := configs.ReadConfig() + skipVault, err := cmd.Flags().GetBool("skip-vault") + if err != nil { + log.Panic(err) + } + skipGitlab, err := cmd.Flags().GetBool("skip-gitlab") + if err != nil { + log.Panic(err) + } + dryRun, err := cmd.Flags().GetBool("dry-run") + if err != nil { + log.Panic(err) + } + pkg.SetupProgress(4) Trackers := make(map[string]*pkg.ActionTracker) @@ -52,62 +62,62 @@ to quickly create a Cobra application.`, metricName := "kubefirst.mgmt_cluster_install.started" metricDomain := viper.GetString("aws.domainname") - if !config.DryRun { + if !dryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) - terraform.ApplyBaseTerraform(cmd, directory) + terraform.ApplyBaseTerraform(dryRun, directory) Trackers[trackerStage20].Tracker.Increment(int64(1)) - softserve.CreateSoftServe(config.KubeConfigPath) + softserve.CreateSoftServe(dryRun, config.KubeConfigPath) Trackers[trackerStage21].Tracker.Increment(int64(1)) - softserve.ConfigureSoftServeAndPush() + softserve.ConfigureSoftServeAndPush(dryRun) Trackers[trackerStage21].Tracker.Increment(int64(1)) - helm.InstallArgocd(config.HomePath) + helm.InstallArgocd(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) if !skipGitlab { //TODO: Confirm if we need to waitgit lab to be ready // OR something, too fast the secret will not be there. - gitlab.AwaitGitlab() - gitlab.ProduceGitlabTokens() + gitlab.AwaitGitlab(dryRun) + gitlab.ProduceGitlabTokens(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) - gitlab.ApplyGitlabTerraform(directory) + gitlab.ApplyGitlabTerraform(dryRun, directory) Trackers[trackerStage22].Tracker.Increment(int64(1)) - gitlab.GitlabKeyUpload() + gitlab.GitlabKeyUpload(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) if !skipVault { - vault.ConfigureVault() + vault.ConfigureVault(dryRun) Trackers[trackerStage23].Tracker.Increment(int64(1)) - vault.AddGitlabOidcApplications() + vault.AddGitlabOidcApplications(dryRun) Trackers[trackerStage23].Tracker.Increment(int64(1)) - gitlab.AwaitGitlab() + gitlab.AwaitGitlab(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) - gitlab.PushGitOpsToGitLab() + gitlab.PushGitOpsToGitLab(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) - gitlab.ChangeRegistryToGitLab() + gitlab.ChangeRegistryToGitLab(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) - gitlab.HydrateGitlabMetaphorRepo() + gitlab.HydrateGitlabMetaphorRepo(dryRun) Trackers[trackerStage23].Tracker.Increment(int64(1)) - token := argocd.GetArgocdAuthToken() - argocd.SyncArgocdApplication("argo-components", token) - argocd.SyncArgocdApplication("gitlab-runner-components", token) - argocd.SyncArgocdApplication("gitlab-runner", token) - argocd.SyncArgocdApplication("atlantis-components", token) - argocd.SyncArgocdApplication("chartmuseum-components", token) + token := argocd.GetArgocdAuthToken(dryRun) + argocd.SyncArgocdApplication(dryRun, "argo-components", token) + argocd.SyncArgocdApplication(dryRun, "gitlab-runner-components", token) + argocd.SyncArgocdApplication(dryRun, "gitlab-runner", token) + argocd.SyncArgocdApplication(dryRun, "atlantis-components", token) + argocd.SyncArgocdApplication(dryRun, "chartmuseum-components", token) } } metricName = "kubefirst.mgmt_cluster_install.completed" - if !config.DryRun { + if !dryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) @@ -117,13 +127,12 @@ to quickly create a Cobra application.`, } func init() { - config := configs.ReadConfig() rootCmd.AddCommand(createCmd) // todo: make this an optional switch and check for it or viper createCmd.Flags().Bool("destroy", false, "destroy resources") - createCmd.PersistentFlags().BoolVarP(&config.DryRun, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") - createCmd.PersistentFlags().BoolVar(&skipVault, "skip-vault", false, "Skip post-gitClient lab install and vault setup") - createCmd.PersistentFlags().BoolVar(&skipGitlab, "skip-gitlab", false, "Skip gitClient lab install and vault setup") + createCmd.Flags().Bool("dry-run", false, "set to dry-run mode, no changes done on cloud provider selected") + createCmd.Flags().Bool("skip-gitlab", false, "Skip GitLab lab install and vault setup") + createCmd.Flags().Bool("skip-vault", false, "Skip post-gitClient lab install and vault setup") } diff --git a/cmd/destroy.go b/cmd/destroy.go index c4a544e1b..1e175bd50 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -26,31 +26,47 @@ if the registry has already been delteted.`, config := configs.ReadConfig() + skipGitlabTerraform, err := cmd.Flags().GetBool("skip-gitlab-terraform") + if err != nil { + log.Panic(err) + } + skipDeleteRegistryApplication, err := cmd.Flags().GetBool("skip-delete-register") + if err != nil { + log.Panic(err) + } + skipBaseTerraform, err := cmd.Flags().GetBool("skip-base-terraform") + if err != nil { + log.Panic(err) + } + destroyBuckets, err := cmd.Flags().GetBool("destroy-buckets") + if err != nil { + log.Panic(err) + } + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") kPortForward.Stdout = os.Stdout kPortForward.Stderr = os.Stderr - err := kPortForward.Start() defer kPortForward.Process.Signal(syscall.SIGTERM) + err = kPortForward.Start() if err != nil { log.Panicf("error: failed to port-forward to gitlab %s", err) } // todo this needs to be removed when we are no longer in the starter account - gitlab.DestroyGitlabTerraform() + gitlab.DestroyGitlabTerraform(skipGitlabTerraform) // delete argocd registry - k8s.DeleteRegistryApplication() - terraform.DestroyBaseTerraform() + k8s.DeleteRegistryApplication(skipDeleteRegistryApplication) + terraform.DestroyBaseTerraform(skipBaseTerraform) //TODO: Remove buckets? Opt-in flag - aws.DestroyBucketsInUse() + aws.DestroyBucketsInUse(destroyBuckets) }, } func init() { - config := configs.ReadConfig() rootCmd.AddCommand(destroyCmd) - destroyCmd.PersistentFlags().BoolVar(&config.SkipGitlabTerraform, "skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") - destroyCmd.PersistentFlags().BoolVar(&config.SkipDeleteRegistryApplication, "skip-delete-register", false, "whether to skip deletion of resgister application ") - destroyCmd.PersistentFlags().BoolVar(&config.SkipBaseTerraform, "skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") - destroyCmd.PersistentFlags().BoolVar(&config.DestroyBuckets, "destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") + destroyCmd.Flags().Bool("skip-gitlab-terraform", false, "whether to skip the terraform destroy against gitlab - note: if you already deleted registry it doesnt exist") + destroyCmd.Flags().Bool("skip-delete-register", false, "whether to skip deletion of register application ") + destroyCmd.Flags().Bool("skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") + destroyCmd.Flags().Bool("destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") } diff --git a/cmd/init.go b/cmd/init.go index 7b077de2b..deafe3b9a 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -29,13 +29,12 @@ to quickly create a Cobra application.`, config := configs.ReadConfig() - var err error - config.DryRun, err = cmd.Flags().GetBool("dry-run") + dryRun, err := cmd.Flags().GetBool("dry-run") if err != nil { log.Panic(err) } - log.Println("dry run enabled:", config.DryRun) + log.Println("dry run enabled:", dryRun) pkg.SetupProgress(10) trackers := pkg.GetTrackers() @@ -54,7 +53,7 @@ to quickly create a Cobra application.`, metricName := "kubefirst.init.started" metricDomain := hostedZoneName - if !config.DryRun { + if !dryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) @@ -96,7 +95,7 @@ to quickly create a Cobra application.`, if !skipHostedZoneCheck { log.Println("skipping hosted zone check") } else { - aws.TestHostedZoneLiveness(hostedZoneName, hostedZoneId) + aws.TestHostedZoneLiveness(dryRun, hostedZoneName, hostedZoneId) } trackers[pkg.TrackerStage2].Tracker.Increment(1) @@ -124,7 +123,7 @@ to quickly create a Cobra application.`, trackers[pkg.TrackerStage6].Tracker.Increment(1) log.Println("calling BucketRand()") - aws.BucketRand() + aws.BucketRand(dryRun) log.Println("BucketRand() complete") log.Println("calling Detokenize()") @@ -134,7 +133,7 @@ to quickly create a Cobra application.`, metricName = "kubefirst.init.completed" - if !config.DryRun { + if !dryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) @@ -147,7 +146,6 @@ to quickly create a Cobra application.`, } func init() { - config := configs.ReadConfig() rootCmd.AddCommand(initCmd) initCmd.Flags().String("hosted-zone-name", "", "the domain to provision the kubefirst platform in") @@ -175,6 +173,6 @@ func init() { log.SetPrefix("LOG: ") log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) - initCmd.PersistentFlags().BoolVarP(&config.DryRun, "dry-run", "s", false, "set to dry-run mode, no changes done on cloud provider selected") + initCmd.Flags().Bool("dry-run", false, "set to dry-run mode, no changes done on cloud provider selected") log.Println("init started") } diff --git a/configs/config.go b/configs/config.go index 76225d3d9..1933f34b6 100644 --- a/configs/config.go +++ b/configs/config.go @@ -8,33 +8,32 @@ import ( "runtime" ) +/** +This is an initial implementation of Config. Please keep in mind we're still working to improve how we handle +environment variables and general config data. +*/ + // Config host application configuration // todo: some of these values can be moved to the .env type Config struct { AwsProfile string `env:"AWS_PROFILE"` - KubefirstLogPath string `env:"KUBEFIRST_LOG_PATH" envDefault:"logs"` - KubectlVersion string `env:"KUBECTL_VERSION" envDefault:"v1.20.0"` - HomePath string LocalOs string LocalArchitecture string + InstallerEmail string + + KubefirstLogPath string `env:"KUBEFIRST_LOG_PATH" envDefault:"logs"` + HomePath string KubectlClientPath string KubeConfigPath string + HelmClientPath string + TerraformPath string + KubectlVersion string `env:"KUBECTL_VERSION" envDefault:"v1.20.0"` TerraformVersion string - TerraformPath string - - HelmClientPath string - HelmVersion string - - DryRun bool - SkipDeleteRegistryApplication bool - DestroyBuckets bool + HelmVersion string + // todo: move it back KubefirstVersion string - InstallerEmail string - - SkipGitlabTerraform bool - SkipBaseTerraform bool } func ReadConfig() *Config { diff --git a/internal/argocd/argocd.go b/internal/argocd/argocd.go index 717a5362a..d4ec8c8b7 100644 --- a/internal/argocd/argocd.go +++ b/internal/argocd/argocd.go @@ -118,9 +118,10 @@ func getArgoCDToken(username string, password string) (string, error) { return token, nil } -func GetArgocdAuthToken() string { +func GetArgocdAuthToken(dryRun bool) string { config := configs.ReadConfig() - if config.DryRun { + + if dryRun { log.Printf("[#99] Dry-run mode, GetArgocdAuthToken skipped.") return "nothing" } @@ -174,9 +175,9 @@ func GetArgocdAuthToken() string { } -func SyncArgocdApplication(applicationName, argocdAuthToken string) { +func SyncArgocdApplication(dryRun bool, applicationName, argocdAuthToken string) { config := configs.ReadConfig() - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, SyncArgocdApplication skipped.") return } diff --git a/internal/aws/aws.go b/internal/aws/aws.go index 0e8433892..c0454ddb6 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -12,7 +12,6 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/cip8/autoname" - "github.com/kubefirst/nebulous/configs" "github.com/spf13/viper" "log" "net" @@ -22,8 +21,8 @@ import ( "time" ) -func BucketRand() { - cfg := configs.ReadConfig() +func BucketRand(dryRun bool) { + sess, err := session.NewSession(&aws.Config{ Region: aws.String(viper.GetString("aws.region"))}, ) @@ -47,7 +46,7 @@ func BucketRand() { regionName := viper.GetString("aws.region") log.Println("region is ", regionName) - if !cfg.DryRun { + if !dryRun { if regionName == "us-east-1" { _, err = s3Client.CreateBucket(&s3.CreateBucketInput{ Bucket: &bucketName, @@ -92,7 +91,7 @@ func GetAccountInfo() { viper.WriteConfig() } -func TestHostedZoneLiveness(hostedZoneName, hostedZoneId string) { +func TestHostedZoneLiveness(dryRun bool, hostedZoneName, hostedZoneId string) { //tracker := progress.Tracker{Message: "testing hosted zone", Total: 25} // todo need to create single client and pass it @@ -122,8 +121,7 @@ func TestHostedZoneLiveness(hostedZoneName, hostedZoneId string) { } if len(recordList.ResourceRecordSets) == 0 { - cfg := configs.ReadConfig() - if !cfg.DryRun { + if !dryRun { record, err := route53Client.ChangeResourceRecordSets(context.TODO(), &route53.ChangeResourceRecordSetsInput{ ChangeBatch: &types.ChangeBatch{ Changes: []types.Change{ @@ -313,9 +311,8 @@ func GetAWSSession() *session.Session { return sess } -func DestroyBucketsInUse() { - cfg := configs.ReadConfig() - if cfg.DestroyBuckets { +func DestroyBucketsInUse(destroyBuckets bool) { + if destroyBuckets { log.Println("Execute: DestroyBucketsInUse") for _, bucket := range ListBucketsInUse() { DestroyBucket(bucket) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 4ef638d0c..f37a0b363 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -93,9 +93,9 @@ func GitlabGeneratePersonalAccessToken(gitlabPodName string) { log.Println("gitlab personal access token generated", gitlabToken) } -func PushGitOpsToGitLab() { +func PushGitOpsToGitLab(dryRun bool) { cfg := configs.ReadConfig() - if cfg.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, PushGitOpsToGitLab skipped.") return } @@ -159,11 +159,10 @@ func PushGitOpsToGitLab() { } -func AwaitGitlab() { - config := configs.ReadConfig() +func AwaitGitlab(dryRun bool) { log.Println("AwaitGitlab called") - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, AwaitGitlab skipped.") return } @@ -182,7 +181,7 @@ func AwaitGitlab() { } } -func ProduceGitlabTokens() { +func ProduceGitlabTokens(dryRun bool) { //TODO: Should this step be skipped if already executed? config := configs.ReadConfig() k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) @@ -194,7 +193,7 @@ func ProduceGitlabTokens() { log.Panic(err.Error()) } log.Println("discovering gitlab toolbox pod") - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, ProduceGitlabTokens skipped.") return } @@ -250,11 +249,11 @@ func ProduceGitlabTokens() { } -func ApplyGitlabTerraform(directory string) { +func ApplyGitlabTerraform(dryRun bool, directory string) { config := configs.ReadConfig() if !viper.GetBool("create.terraformapplied.gitlab") { log.Println("Executing ApplyGitlabTerraform") - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, ApplyGitlabTerraform skipped.") return } @@ -283,13 +282,13 @@ func ApplyGitlabTerraform(directory string) { } } -func GitlabKeyUpload() { - config := configs.ReadConfig() +func GitlabKeyUpload(dryRun bool) { + // upload ssh public key if !viper.GetBool("gitlab.keyuploaded") { log.Println("Executing GitlabKeyUpload") log.Println("uploading ssh public key for gitlab user") - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, GitlabKeyUpload skipped.") return } @@ -318,7 +317,7 @@ func GitlabKeyUpload() { } } -func DestroyGitlabTerraform() { +func DestroyGitlabTerraform(skipGitlabTerraform bool) { config := configs.ReadConfig() log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") // kubeconfig := os.Getenv("HOME") + "/.kube/config" @@ -348,7 +347,7 @@ func DestroyGitlabTerraform() { os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") - if !config.SkipGitlabTerraform { + if !skipGitlabTerraform { tfInitGitlabCmd := exec.Command(config.TerraformPath, "init") tfInitGitlabCmd.Stdout = os.Stdout tfInitGitlabCmd.Stderr = os.Stderr @@ -372,10 +371,10 @@ func DestroyGitlabTerraform() { } } -func ChangeRegistryToGitLab() { +func ChangeRegistryToGitLab(dryRun bool) { config := configs.ReadConfig() if !viper.GetBool("gitlab.registry") { - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, ChangeRegistryToGitLab skipped.") return } @@ -478,11 +477,11 @@ func ChangeRegistryToGitLab() { } } -func HydrateGitlabMetaphorRepo() { +func HydrateGitlabMetaphorRepo(dryRun bool) { cfg := configs.ReadConfig() //TODO: Should this be skipped if already executed? if !viper.GetBool("create.gitlabmetaphor.cloned") { - if cfg.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, hydrateGitlabMetaphorRepo skipped.") return } diff --git a/internal/helm/helm.go b/internal/helm/helm.go index 95ba8246d..c22e5f8d4 100644 --- a/internal/helm/helm.go +++ b/internal/helm/helm.go @@ -9,10 +9,10 @@ import ( "os/exec" ) -func InstallArgocd(home string) { +func InstallArgocd(dryRun bool) { config := configs.ReadConfig() if !viper.GetBool("create.argocd.helm") { - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, helmInstallArgocd skipped.") return } @@ -33,7 +33,7 @@ func InstallArgocd(home string) { log.Panicf("error: could not helm repo update %s", err) } - helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", home), "argo/argo-cd") + helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", config.HomePath), "argo/argo-cd") helmInstallArgocdCmd.Stdout = os.Stdout helmInstallArgocdCmd.Stderr = os.Stderr err = helmInstallArgocdCmd.Run() diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index ab3049e10..28a6939bd 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -66,9 +66,9 @@ func GetSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key strin return string(secret.Data[key]) } -func DeleteRegistryApplication() { +func DeleteRegistryApplication(skipDeleteRegistryApplication bool) { config := configs.ReadConfig() - if !config.SkipDeleteRegistryApplication { + if !skipDeleteRegistryApplication { log.Println("starting port forward to argocd server and deleting registry") kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") kPortForward.Stdout = os.Stdout diff --git a/internal/softserve/softserve.go b/internal/softserve/softserve.go index ae80fde43..5bfa04d64 100644 --- a/internal/softserve/softserve.go +++ b/internal/softserve/softserve.go @@ -18,11 +18,11 @@ import ( "time" ) -func CreateSoftServe(kubeconfigPath string) { +func CreateSoftServe(dryRun bool, kubeconfigPath string) { config := configs.ReadConfig() if !viper.GetBool("create.softserve.create") { log.Println("Executing CreateSoftServe") - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, CreateSoftServe skipped.") return } @@ -52,12 +52,12 @@ func CreateSoftServe(kubeconfigPath string) { } -func ConfigureSoftServeAndPush() { +func ConfigureSoftServeAndPush(dryRun bool) { config := configs.ReadConfig() configureAndPushFlag := viper.GetBool("create.softserve.configure") if configureAndPushFlag != true { log.Println("Executing ConfigureSoftServeAndPush") - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, ConfigureSoftServeAndPush skipped.") return } diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index b41a19cab..b7134164b 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -4,19 +4,18 @@ import ( "fmt" "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/pkg" - "github.com/spf13/cobra" "github.com/spf13/viper" "log" "os" "strings" ) -func ApplyBaseTerraform(cmd *cobra.Command, directory string) { +func ApplyBaseTerraform(dryRun bool, directory string) { config := configs.ReadConfig() applyBase := viper.GetBool("create.terraformapplied.base") if applyBase != true { log.Println("Executing ApplyBaseTerraform") - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, applyBaseTerraform skipped.") return } @@ -53,9 +52,9 @@ func ApplyBaseTerraform(cmd *cobra.Command, directory string) { } } -func DestroyBaseTerraform() { +func DestroyBaseTerraform(skipBaseTerraform bool) { config := configs.ReadConfig() - if !config.SkipBaseTerraform { + if !skipBaseTerraform { directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) err := os.Chdir(directory) if err != nil { diff --git a/internal/vault/vault.go b/internal/vault/vault.go index daa21f006..f65ba0777 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -39,10 +39,10 @@ func GetVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) (string, e return vaultRootToken, nil } -func ConfigureVault() { +func ConfigureVault(dryRun bool) { config := configs.ReadConfig() if !viper.GetBool("create.terraformapplied.vault") { - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, configureVault skipped.") return } @@ -130,10 +130,10 @@ func ConfigureVault() { } } -func AddGitlabOidcApplications() { - config := configs.ReadConfig() +func AddGitlabOidcApplications(dryRun bool) { + //TODO: Should this skipped if already executed. - if config.DryRun { + if dryRun { log.Printf("[#99] Dry-run mode, addGitlabOidcApplications skipped.") return } From 96d9b6dc0a4d64db7266ec2a26a1004cddc8fa48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Mon, 11 Jul 2022 09:16:45 -0300 Subject: [PATCH 052/107] refactor: update init progress bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/init.go | 10 +++++++--- internal/aws/aws.go | 9 +++++++-- internal/downloadManager/download.go | 9 +-------- internal/telemetry/telemetry.go | 3 +++ pkg/progress_bar.go | 8 ++++++++ 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index deafe3b9a..af72a5f7e 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -45,7 +45,7 @@ to quickly create a Cobra application.`, trackers[pkg.TrackerStage4] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage4, 1)} trackers[pkg.TrackerStage5] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage5, 3)} trackers[pkg.TrackerStage6] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage6, 1)} - trackers[pkg.TrackerStage7] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage7, 4)} + trackers[pkg.TrackerStage7] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage7, 3)} trackers[pkg.TrackerStage8] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage8, 1)} trackers[pkg.TrackerStage9] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage9, 1)} infoCmd.Run(cmd, args) @@ -56,7 +56,7 @@ to quickly create a Cobra application.`, if !dryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { - log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) + log.Printf("[#99999] Dry-run mode, telemetry skipped: %s", metricName) } // todo need to check flags and create config @@ -110,10 +110,12 @@ to quickly create a Cobra application.`, trackers[pkg.TrackerStage4].Tracker.Increment(1) log.Println("calling download()") + trackers[pkg.TrackerStage5].Tracker.Increment(1) err = downloadManager.DownloadTools(config, trackers) if err != nil { log.Panic(err) } + trackers[pkg.TrackerStage5].Tracker.Increment(1) log.Println("download() complete") @@ -123,7 +125,9 @@ to quickly create a Cobra application.`, trackers[pkg.TrackerStage6].Tracker.Increment(1) log.Println("calling BucketRand()") - aws.BucketRand(dryRun) + trackers[pkg.TrackerStage7].Tracker.Increment(1) + aws.BucketRand(dryRun, trackers) + trackers[pkg.TrackerStage7].Tracker.Increment(1) log.Println("BucketRand() complete") log.Println("calling Detokenize()") diff --git a/internal/aws/aws.go b/internal/aws/aws.go index c0454ddb6..7c231ef7a 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -12,6 +12,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/cip8/autoname" + "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" "log" "net" @@ -21,7 +22,7 @@ import ( "time" ) -func BucketRand(dryRun bool) { +func BucketRand(dryRun bool, trackers map[string]*pkg.ActionTracker) { sess, err := session.NewSession(&aws.Config{ Region: aws.String(viper.GetString("aws.region"))}, @@ -36,6 +37,8 @@ func BucketRand(dryRun bool) { randomName := strings.ReplaceAll(autoname.Generate(), "_", "-") viper.Set("bucket.rand", randomName) + trackers[pkg.TrackerStage7].Tracker.Increment(int64(1)) + buckets := strings.Fields("state-store argo-artifacts gitlab-backup chartmuseum") for _, bucket := range buckets { bucketExists := viper.GetBool(fmt.Sprintf("bucket.%s.created", bucket)) @@ -71,7 +74,6 @@ func BucketRand(dryRun bool) { viper.WriteConfig() } log.Printf("bucket %s exists", viper.GetString(fmt.Sprintf("bucket.%s.name", bucket))) - //Trackers[trackerStage7].Tracker.Increment(int64(1)) } } @@ -217,6 +219,8 @@ func TestHostedZoneLiveness(dryRun bool, hostedZoneName, hostedZoneId string) { func GetDNSInfo(hostedZoneName string) string { + log.Println("GetDNSInfo (working...)") + cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Println("failed to load configuration, error:", err) @@ -241,6 +245,7 @@ func GetDNSInfo(hostedZoneName string) string { viper.WriteConfig() } } + log.Println("GetDNSInfo (done)") return zoneId } diff --git a/internal/downloadManager/download.go b/internal/downloadManager/download.go index 2a6cb6732..cdd2aef7d 100644 --- a/internal/downloadManager/download.go +++ b/internal/downloadManager/download.go @@ -56,12 +56,6 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke log.Panicf("failed to call kubectlVersionCmd.Run(): %v", err) } - const trackerStage20 = "0 - Apply Base" - const trackerStage5 = "6 - DownloadTools Tools" - - trackers[trackerStage20] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage20, 1)} - trackers[trackerStage5].Tracker.Increment(1) - // todo: adopt latest helmVersion := "v3.9.0" terraformVersion := config.TerraformVersion @@ -93,7 +87,7 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke return err } - trackers[trackerStage5].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage5].Tracker.Increment(int64(1)) helmVersion := config.HelmVersion helmDownloadUrl := fmt.Sprintf( @@ -137,7 +131,6 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke if errHelm != nil { log.Panicf("error executing helm version command: %v", err) } - trackers[trackerStage5].Tracker.Increment(int64(1)) return nil } diff --git a/internal/telemetry/telemetry.go b/internal/telemetry/telemetry.go index eebffe905..6c9f0f8c6 100644 --- a/internal/telemetry/telemetry.go +++ b/internal/telemetry/telemetry.go @@ -10,6 +10,7 @@ import ( // SendTelemetry post telemetry data func SendTelemetry(domain, metricName string) { + log.Println("SendTelemetry (working...)") url := "https://metaphor-go-production.kubefirst.io/telemetry" method := "POST" @@ -35,4 +36,6 @@ func SendTelemetry(domain, metricName string) { body, err := ioutil.ReadAll(res.Body) log.Println(string(body)) + + log.Println("SendTelemetry (done)") } diff --git a/pkg/progress_bar.go b/pkg/progress_bar.go index 03f088828..b3ba22994 100644 --- a/pkg/progress_bar.go +++ b/pkg/progress_bar.go @@ -22,6 +22,14 @@ const TrackerStage7 = "8 - Create Buckets" const TrackerStage8 = "9 - Detokenize" const TrackerStage9 = "10 - Send Telemetry" +//const trackerStage5 = "6 - DownloadTools Tools" + +const TrackerStage20 = "0 - Apply Base" + +//const trackerStage21 = "1 - Temporary SCM Install" +//const trackerStage22 = "2 - Argo/Final SCM Install" +//const trackerStage23 = "3 - Final Setup" + var ( pw progress.Writer Trackers map[string]*ActionTracker From 97ea13e7531acf1f3fbb99b7313c21058b67c1d8 Mon Sep 17 00:00:00 2001 From: Thiago Pagotto Date: Mon, 11 Jul 2022 10:19:11 -0300 Subject: [PATCH 053/107] added cluster-name and gitops-version Signed-off-by: Thiago Pagotto --- cmd/init.go | 26 +++++++++++++++++++++++--- internal/gitClient/git.go | 15 +++++++++++---- pkg/helpers.go | 5 ++++- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index af72a5f7e..721da6a0e 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -2,6 +2,10 @@ package cmd import ( "fmt" + "log" + "strings" + "time" + "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/internal/aws" "github.com/kubefirst/nebulous/internal/downloadManager" @@ -10,9 +14,6 @@ import ( "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" - "log" - "strings" - "time" ) // initCmd represents the init command @@ -90,6 +91,22 @@ to quickly create a Cobra application.`, trackers[pkg.TrackerStage0].Tracker.Increment(1) trackers[pkg.TrackerStage1].Tracker.Increment(1) + //cluster name + clusterName, err := cmd.Flags().GetString("cluster-name") + if err != nil { + log.Panic(err) + } + viper.Set("cluster-name", clusterName) + log.Println("cluster-name:", clusterName) + + //version-gitops + versionGitOps, err := cmd.Flags().GetString("version-gitops") + if err != nil { + log.Panic(err) + } + viper.Set("version-gitops", versionGitOps) + log.Println("version-gitops:", versionGitOps) + // todo: this doesn't default to testing the dns check skipHostedZoneCheck := viper.GetBool("init.hostedzonecheck.enabled") if !skipHostedZoneCheck { @@ -179,4 +196,7 @@ func init() { initCmd.Flags().Bool("dry-run", false, "set to dry-run mode, no changes done on cloud provider selected") log.Println("init started") + + initCmd.Flags().String("cluster-name", "k1st", "the cluster name, used to identify resources on cloud provider") + initCmd.Flags().String("version-gitops", "main", "version/branch used on git clone") } diff --git a/internal/gitClient/git.go b/internal/gitClient/git.go index d5883b59a..a5d46eede 100644 --- a/internal/gitClient/git.go +++ b/internal/gitClient/git.go @@ -2,14 +2,17 @@ package gitClient import ( "fmt" + "log" + "time" + "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/pkg" + "github.com/spf13/viper" "golang.org/x/crypto/ssh" - "log" - "time" ) func CloneGitOpsRepo() { @@ -18,10 +21,14 @@ func CloneGitOpsRepo() { url := "https://github.com/kubefirst/gitops-template" directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) - log.Println("gitClient clone", url, directory) + versionGitOps := viper.GetString("version-gitops") + + log.Println("git clone -b ", versionGitOps, url, directory) _, err := git.PlainClone(directory, false, &git.CloneOptions{ - URL: url, + URL: url, + ReferenceName: plumbing.NewBranchReferenceName(versionGitOps), + SingleBranch: true, }) if err != nil { log.Panicf("error cloning gitops-template repository from github, error is: %s", err) diff --git a/pkg/helpers.go b/pkg/helpers.go index 535f5539d..f07478969 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -2,12 +2,13 @@ package pkg import ( "fmt" - "github.com/spf13/viper" "io/ioutil" "log" "os" "path/filepath" "strings" + + "github.com/spf13/viper" ) func Detokenize(path string) { @@ -64,6 +65,7 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { adminEmail := viper.GetString("adminemail") awsAccountId := viper.GetString("aws.accountid") kmsKeyId := viper.GetString("vault.kmskeyid") + clusterName := viper.GetString("cluster-name") newContents = strings.Replace(newContents, "", strings.TrimSpace(botPublicKey), -1) newContents = strings.Replace(newContents, "", bucketStateStore, -1) @@ -78,6 +80,7 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { if kmsKeyId != "" { newContents = strings.Replace(newContents, "", kmsKeyId, -1) } + newContents = strings.Replace(newContents, "", clusterName, -1) if viper.GetBool("create.terraformapplied.gitlab") { newContents = strings.Replace(newContents, "", hostedzonename, -1) From d756e18746852b7301651f26bb499dcc17e79c46 Mon Sep 17 00:00:00 2001 From: Thiago Pagotto Date: Mon, 11 Jul 2022 10:26:23 -0300 Subject: [PATCH 054/107] blank space Signed-off-by: Thiago Pagotto --- internal/gitClient/git.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/gitClient/git.go b/internal/gitClient/git.go index a5d46eede..cc6516da6 100644 --- a/internal/gitClient/git.go +++ b/internal/gitClient/git.go @@ -31,7 +31,7 @@ func CloneGitOpsRepo() { SingleBranch: true, }) if err != nil { - log.Panicf("error cloning gitops-template repository from github, error is: %s", err) + log.Panicf("error cloning gitops-template repository from github, error is: %s", err) } log.Println("downloaded gitops repo from template to directory", config.HomePath, "/.kubefirst/gitops") From f20eb401a1d890bca95470005fc3456eb1865409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Mon, 11 Jul 2022 11:07:20 -0300 Subject: [PATCH 055/107] chore: merge arcocdsync and clean commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/argocdSync.go | 43 +++++++++++++++++++++++++++++++++++++++++++ cmd/clean.go | 15 ++++++++++++--- 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 cmd/argocdSync.go diff --git a/cmd/argocdSync.go b/cmd/argocdSync.go new file mode 100644 index 000000000..bc972d672 --- /dev/null +++ b/cmd/argocdSync.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "log" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// argocdSyncCmd represents the argocdSync command +var argocdSyncCmd = &cobra.Command{ + Use: "argocdSync", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + + applicationName, _ := cmd.Flags().GetString("app-name") + refreshToken, _ := cmd.Flags().GetBool("refresh-token") + + authToken := viper.GetString("argocd.admin.apitoken") + + if !refreshToken && authToken == "" { + log.Panic("uh oh - no argocd auth token found in config, try again with `--refresh-token` ") + } else { + log.Println("getting a new argocd session token") + authToken = getArgocdAuthToken() + } + log.Printf("syncing the %s application", applicationName) + syncArgocdApplication(applicationName, authToken) + }, +} + +func init() { + rootCmd.AddCommand(argocdSyncCmd) + argocdSyncCmd.Flags().String("app-name", "", "gets a new argocd session token") + argocdSyncCmd.MarkFlagRequired("app-name") + argocdSyncCmd.Flags().Bool("refresh-token", false, "gets a new argocd session token") +} diff --git a/cmd/clean.go b/cmd/clean.go index 03d84008b..96265a411 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -12,7 +12,7 @@ import ( // cleanCmd represents the clean command var cleanCmd = &cobra.Command{ Use: "clean", - Short: "A brief description of your command", + Short: "removes all kubefirst resources locally for new execution", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your command. For example: @@ -20,6 +20,8 @@ Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { + // todo delete the s3 buckets associated with the ~/.flare file + // todo ask for user input to verify deletion? config := configs.ReadConfig() log.Println("removing $HOME/.kubefirst and $HOME/.flare") @@ -27,8 +29,15 @@ to quickly create a Cobra application.`, os.RemoveAll(fmt.Sprintf("%s/.kubefirst", config.HomePath)) os.Remove(fmt.Sprintf("%s/.flare", config.HomePath)) log.Println("removed $HOME/.kubefirst and $HOME/.flare") - // todo log.Println("proceed to kubefirst create ") - log.Println("proceed to flare nebulous create ") + if err := os.Mkdir(fmt.Sprintf("%s/.kubefirst", config.HomePath), os.ModePerm); err != nil { + log.Panicf("error: could not create directory $HOME/.kubefirst - it must exist to continue %s", err) + } + toolsDir := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) + if err := os.Mkdir(toolsDir, os.ModePerm); err != nil { + log.Panicf("error: could not create directory $HOME/.kubefirst/tools - it must exist to continue %s", err) + } + + log.Println("created $HOME/.kubefirst and $HOME/.kubefirst/tools - proceed to `kubefirst init`") }, } From 52e2a560cb97276b1dc5d9b98a183821bb3c0fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Mon, 11 Jul 2022 14:34:28 -0300 Subject: [PATCH 056/107] chore: merge in progress, code is breaking, now we can start validation and fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 223 +++++++++++++++++++++++++-- cmd/destroy.go | 58 ++++++- cmd/init.go | 53 ++++++- cmd/kubernetes.go | 162 +++++++++++++++++++ cmd/root.go | 13 +- configs/kubefirstDirectory.go | 2 +- internal/argocd/argocd.go | 75 ++++----- internal/aws/aws.go | 10 +- internal/downloadManager/download.go | 1 + internal/gitlab/gitlab.go | 132 ++++++++++++---- internal/softserve/softserve.go | 46 ++---- internal/vault/vault.go | 5 +- pkg/helpers.go | 4 +- pkg/keys.go | 15 ++ pkg/progress_bar.go | 4 +- 15 files changed, 656 insertions(+), 147 deletions(-) create mode 100644 cmd/kubernetes.go diff --git a/cmd/create.go b/cmd/create.go index 2efbf09fc..0c9a4a423 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/argocd" "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/helm" "github.com/kubefirst/nebulous/internal/softserve" @@ -13,7 +12,11 @@ import ( "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" "log" + "os" + "syscall" "time" ) @@ -25,7 +28,7 @@ const trackerStage23 = "3 - Final Setup" // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", - Short: "A brief description of your command", + Short: "create a kubefirst management cluster", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your command. For example: @@ -60,7 +63,7 @@ to quickly create a Cobra application.`, infoCmd.Run(cmd, args) metricName := "kubefirst.mgmt_cluster_install.started" - metricDomain := viper.GetString("aws.domainname") + metricDomain := viper.GetString("aws.hostedzonename") if !dryRun { telemetry.SendTelemetry(metricDomain, metricName) @@ -71,17 +74,173 @@ to quickly create a Cobra application.`, directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) terraform.ApplyBaseTerraform(dryRun, directory) Trackers[trackerStage20].Tracker.Increment(int64(1)) + + //! soft-serve was just applied + softserve.CreateSoftServe(dryRun, config.KubeConfigPath) + waitForNamespaceandPods("soft-serve", "app=soft-serve") + // todo this should be replaced with something more intelligent + log.Println("waiting for soft-serve installation to complete...") + time.Sleep(60 * time.Second) + + kPortForwardSoftServe := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") + kPortForwardSoftServe.Stdout = os.Stdout + kPortForwardSoftServe.Stderr = os.Stderr + err := kPortForwardSoftServe.Start() + defer kPortForwardSoftServe.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to soft-serve %s", err) + } + time.Sleep(20 * time.Second) + Trackers[trackerStage21].Tracker.Increment(int64(1)) softserve.ConfigureSoftServeAndPush(dryRun) Trackers[trackerStage21].Tracker.Increment(int64(1)) helm.InstallArgocd(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) + //! argocd was just helm installed + x := 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "get", "namespace/argocd") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + 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++ { + kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "get", "pods", "-l", "app.kubernetes.io/name=argocd-server") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + 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, continuing") + time.Sleep(15 * time.Second) + break + } + } + + kPortForwardArgocd := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") + kPortForwardArgocd.Stdout = os.Stdout + kPortForwardArgocd.Stderr = os.Stderr + err = kPortForwardArgocd.Start() + defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd in main thread %s", err) + } + + log.Println("sleeping for 45 seconds, hurry up jared") + time.Sleep(45 * time.Second) + + log.Println("setting argocd credentials") + setArgocdCreds() + log.Println("getting an argocd auth token") + token := getArgocdAuthToken() + log.Println("syncing the registry application") + syncArgocdApplication("registry", token) + // todo, need to stall until the registry has synced, then get to ui asap + + //! skip this if syncing from argocd and not helm installing + log.Printf("sleeping for 30 seconds, hurry up jared sign into argocd %s", viper.GetString("argocd.admin.password")) + time.Sleep(30 * time.Second) + + //! + //* we need to stop here and wait for the vault namespace to exist and the vault pod to be ready + //! + x = 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "get", "namespace/vault") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + 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 + } + } + x = 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "get", "pods", "-l", "vault-initialized=true") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + 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 + } + } + kPortForwardVault := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + kPortForwardVault.Stdout = os.Stdout + kPortForwardVault.Stderr = os.Stderr + err = kPortForwardVault.Start() + defer kPortForwardVault.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to vault in main thread %s", err) + } + + x = 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "get", "namespace/gitlab") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + if err != nil { + log.Println("Waiting gitlab namespace to be born") + time.Sleep(10 * time.Second) + } else { + log.Println("gitlab namespace found, continuing") + time.Sleep(5 * time.Second) + break + } + } + x = 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pods", "-l", "app=webservice") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + if err != nil { + log.Println("Waiting gitlab pods to be born") + time.Sleep(10 * time.Second) + } else { + log.Println("gitlab pods found, continuing") + time.Sleep(15 * time.Second) + break + } + } + log.Println("waiting for gitlab") + waitForGitlab() + log.Println("gitlab is ready!") + kPortForwardGitlab := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + kPortForwardGitlab.Stdout = os.Stdout + kPortForwardGitlab.Stderr = os.Stderr + err = kPortForwardGitlab.Start() + defer kPortForwardGitlab.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) + } + if !skipGitlab { - //TODO: Confirm if we need to waitgit lab to be ready + // TODO: Confirm if we need to waitgit lab to be ready // OR something, too fast the secret will not be there. - gitlab.AwaitGitlab(dryRun) gitlab.ProduceGitlabTokens(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlab.ApplyGitlabTerraform(dryRun, directory) @@ -90,28 +249,51 @@ to quickly create a Cobra application.`, Trackers[trackerStage22].Tracker.Increment(int64(1)) if !skipVault { + + log.Println("waiting for vault unseal") + waitForVaultUnseal() + log.Println("vault unseal condition met - continuing") + + log.Println("configuring vault") vault.ConfigureVault(dryRun) + log.Println("vault configured") + + log.Println("creating vault configured secret") + createVaultConfiguredSecret() + log.Println("vault-configured secret created") + Trackers[trackerStage23].Tracker.Increment(int64(1)) vault.AddGitlabOidcApplications(dryRun) Trackers[trackerStage23].Tracker.Increment(int64(1)) + + log.Println("waiting for gitlab dns to propagate before continuing") gitlab.AwaitGitlab(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) + log.Println("pushing gitops repo to origin gitlab") + // refactor: sounds like a new functions, should PushGitOpsToGitLab be renamed/update signature? + pushGitRepo("gitlab", "gitops") // todo need to handle if this was already pushed, errors on failure gitlab.PushGitOpsToGitLab(dryRun) + Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlab.ChangeRegistryToGitLab(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) + // refactor: should this be removed? gitlab.HydrateGitlabMetaphorRepo(dryRun) Trackers[trackerStage23].Tracker.Increment(int64(1)) - token := argocd.GetArgocdAuthToken(dryRun) - argocd.SyncArgocdApplication(dryRun, "argo-components", token) - argocd.SyncArgocdApplication(dryRun, "gitlab-runner-components", token) - argocd.SyncArgocdApplication(dryRun, "gitlab-runner", token) - argocd.SyncArgocdApplication(dryRun, "atlantis-components", token) - argocd.SyncArgocdApplication(dryRun, "chartmuseum-components", token) + // todo triage / force apply the contents adjusting + // todo kind: Application .repoURL: + + // refactor: should this be deleted? + //token := argocd.GetArgocdAuthToken(dryRun) + //argocd.SyncArgocdApplication(dryRun, "argo-components", token) + //argocd.SyncArgocdApplication(dryRun, "gitlab-runner-components", token) + //argocd.SyncArgocdApplication(dryRun, "gitlab-runner", token) + //argocd.SyncArgocdApplication(dryRun, "atlantis-components", token) + //argocd.SyncArgocdApplication(dryRun, "chartmuseum-components", token) } } @@ -136,3 +318,22 @@ func init() { createCmd.Flags().Bool("skip-vault", false, "Skip post-gitClient lab install and vault setup") } + +// todo: move it to internals/ArgoCD +func setArgocdCreds() { + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + if err != nil { + panic(err.Error()) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) + } + argocdSecretClient = clientset.CoreV1().Secrets("argocd") + + argocdPassword := getSecretValue(argocdSecretClient, "argocd-initial-admin-secret", "password") + + viper.Set("argocd.admin.password", argocdPassword) + viper.Set("argocd.admin.username", "admin") + viper.WriteConfig() +} diff --git a/cmd/destroy.go b/cmd/destroy.go index 1e175bd50..38cb40ff8 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -1,16 +1,19 @@ package cmd import ( + "fmt" "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/internal/aws" "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/k8s" "github.com/kubefirst/nebulous/internal/terraform" "github.com/spf13/cobra" + "github.com/spf13/viper" "log" "os" "os/exec" "syscall" + "time" ) // destroyCmd represents the destroy command @@ -18,10 +21,10 @@ var destroyCmd = &cobra.Command{ Use: "destroy", Short: "destroy the kubefirst management cluster", Long: `destory the kubefirst management cluster -and all of the components in k8s. +and all of the components in kubernetes. Optional: skip gitlab terraform -if the registry has already been delteted.`, +if the registry has already been deleted.`, Run: func(cmd *cobra.Command, args []string) { config := configs.ReadConfig() @@ -49,14 +52,38 @@ if the registry has already been delteted.`, defer kPortForward.Process.Signal(syscall.SIGTERM) err = kPortForward.Start() if err != nil { - log.Panicf("error: failed to port-forward to gitlab %s", err) + log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) } - // todo this needs to be removed when we are no longer in the starter account + + kPortForwardArgocd := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") + kPortForwardArgocd.Stdout = os.Stdout + kPortForwardArgocd.Stderr = os.Stderr + err = kPortForwardArgocd.Start() + defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd in main thread %s", err) + } + kPortForwardVault := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + kPortForwardVault.Stdout = os.Stdout + kPortForwardVault.Stderr = os.Stderr + err = kPortForwardVault.Start() + defer kPortForwardVault.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to vault in main thread %s", err) + } + log.Println("destroying gitlab terraform") + gitlab.DestroyGitlabTerraform(skipGitlabTerraform) + log.Println("gitlab terraform destruction complete") + log.Println("deleting registry application in argocd") + // delete argocd registry k8s.DeleteRegistryApplication(skipDeleteRegistryApplication) + log.Println("registry application deleted") + log.Println("terraform destroy base") terraform.DestroyBaseTerraform(skipBaseTerraform) - //TODO: Remove buckets? Opt-in flag + log.Println("terraform base destruction complete") + //TODO: move this step to `kubefirst clean` command and empty buckets and delete aws.DestroyBucketsInUse(destroyBuckets) }, } @@ -70,3 +97,24 @@ func init() { destroyCmd.Flags().Bool("skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") destroyCmd.Flags().Bool("destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") } + +func deleteArgocdRegistryApplication() { + if !skipDeleteRegistryApplication { + + log.Println("refreshing argocd session token") + getArgocdAuthToken() + + url := "https://localhost:8080/api/v1/applications/registry" + argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) + argoCdAppSync.Stdout = os.Stdout + argoCdAppSync.Stderr = os.Stderr + err := argoCdAppSync.Run() + if err != nil { + log.Panicf("error: delete registry applicatoin from argocd failed: %s", err) + } + log.Println("waiting for argocd deletion to complete") + time.Sleep(300 * time.Second) + } else { + log.Println("skip: deleteRegistryApplication") + } +} diff --git a/cmd/init.go b/cmd/init.go index 721da6a0e..a9b238cb6 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -19,7 +19,7 @@ import ( // initCmd represents the init command var initCmd = &cobra.Command{ Use: "init", - Short: "A brief description of your command", + Short: "initialize your local machine to execute `create`", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your command. For example: @@ -57,7 +57,7 @@ to quickly create a Cobra application.`, if !dryRun { telemetry.SendTelemetry(metricDomain, metricName) } else { - log.Printf("[#99999] Dry-run mode, telemetry skipped: %s", metricName) + log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } // todo need to check flags and create config @@ -70,7 +70,9 @@ to quickly create a Cobra application.`, } log.Println("hostedZoneName:", hostedZoneName) viper.Set("aws.hostedzonename", hostedZoneName) - viper.WriteConfig() + viper.Set("argocd.local.service", "http://localhost:8080") + viper.Set("gitlab.local.service", "http://localhost:8888") + viper.Set("vault.local.service", "http://localhost:8200") // admin email // used for letsencrypt notifications and the gitlab root account adminEmail, _ := cmd.Flags().GetString("admin-email") @@ -83,6 +85,22 @@ to quickly create a Cobra application.`, viper.Set("aws.region", region) log.Println("region:", region) + viper.WriteConfig() + + // refactor: confirm it (start) + //! tracker 0 + log.Println("installing kubefirst dependencies") + download() + log.Println("dependency installation complete") + Trackers[trackerStage0].Tracker.Increment(int64(1)) + + //! tracker 1 + log.Println("getting aws account information") + getAccountInfo() + log.Printf("aws account id: %s\naws user arn: %s", viper.GetString("aws.accountid"), viper.GetString("aws.userarn")) + Trackers[trackerStage1].Tracker.Increment(int64(1)) + // refactor: confirm it (end) + // hosted zone id // so we don't have to keep looking it up from the domain name to use it hostedZoneId := aws.GetDNSInfo(hostedZoneName) @@ -116,9 +134,9 @@ to quickly create a Cobra application.`, } trackers[pkg.TrackerStage2].Tracker.Increment(1) - log.Println("calling CreateSshKeyPair() ") + log.Println("creating an ssh key pair for your new cloud infrastructure") pkg.CreateSshKeyPair() - log.Println("CreateSshKeyPair() complete") + log.Println("ssh key pair creation complete") trackers[pkg.TrackerStage3].Tracker.Increment(1) log.Println("calling cloneGitOpsRepo()") @@ -143,6 +161,12 @@ to quickly create a Cobra application.`, log.Println("calling BucketRand()") trackers[pkg.TrackerStage7].Tracker.Increment(1) + + //! tracker 4 + //* should we consider going down to a single bucket + //* for state and artifacts on open source? + //* hitting a bucket limit on an install might deter someone + log.Println("creating buckets for state and artifacts") aws.BucketRand(dryRun, trackers) trackers[pkg.TrackerStage7].Tracker.Increment(1) log.Println("BucketRand() complete") @@ -152,6 +176,23 @@ to quickly create a Cobra application.`, log.Println("Detokenize() complete") trackers[pkg.TrackerStage8].Tracker.Increment(1) + // TODO: get the below line added as a legit flag, don't merge with any value except kubefirst + gitopsTemplateGithubOrgOverride := "kubefirst" // <-- discussion point + log.Printf("cloning and detokenizing the gitops-template repository") + if gitopsTemplateGithubOrgOverride != "" { + log.Printf("using --gitops-template-gh-org=%s", gitopsTemplateGithubOrgOverride) + } + + //! tracker 6 + prepareKubefirstTemplateRepo(gitopsTemplateGithubOrgOverride, "gitops") + log.Println("clone and detokenization of gitops-template repository complete") + Trackers[trackerStage6].Tracker.Increment(int64(1)) + //! tracker 7 + log.Printf("cloning and detokenizing the metaphor-template repository") + prepareKubefirstTemplateRepo("kubefirst", "metaphor") + log.Println("clone and detokenization of metaphor-template repository complete") + Trackers[trackerStage7].Tracker.Increment(int64(1)) + metricName = "kubefirst.init.completed" if !dryRun { @@ -161,6 +202,8 @@ to quickly create a Cobra application.`, } viper.WriteConfig() + + //! tracker 8 trackers[pkg.TrackerStage9].Tracker.Increment(1) time.Sleep(time.Millisecond * 100) }, diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go new file mode 100644 index 000000000..d98256ad2 --- /dev/null +++ b/cmd/kubernetes.go @@ -0,0 +1,162 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "log" + "os" + "os/exec" + "time" + + "github.com/spf13/viper" + metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" +) + +var vaultRootToken string +var gitlabToolboxPodName string + +// API client for managing secrets & pods +var gitlabSecretClient coreV1Types.SecretInterface +var vaultSecretClient coreV1Types.SecretInterface +var argocdSecretClient coreV1Types.SecretInterface +var gitlabPodsClient coreV1Types.PodInterface + +func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { + pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) + if err != nil { + fmt.Println(err) + } + + gitlabToolboxPodName = pods.Items[0].Name + + return gitlabToolboxPodName +} + +func waitForVaultUnseal() { + vaultReady := viper.GetBool("create.vault.ready") + if !vaultReady { + var output bytes.Buffer + // todo - add a viper.GetBool() check to the beginning of this function + // todo write in golang? see here -> https://github.com/bcreane/k8sutils/blob/master/utils.go + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "wait", "--for=condition=ready", "pod", "-l", "vault-initialized=true", "--timeout=300s") + k.Stdout = &output + k.Stderr = os.Stderr + err := k.Run() + if err != nil { + log.Panicf("failed to execute kubectl wait for vault pods with label vault-initialized=true: %s \n%s", output, err) + } + log.Printf("the output is: %s", output.String()) + } else { + log.Println("vault is ready") + } + +} + +func waitForGitlab() { + var output bytes.Buffer + // todo - add a viper.GetBool() check to the beginning of this function + // todo write in golang? see here -> https://github.com/bcreane/k8sutils/blob/master/utils.go + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "wait", "--for=condition=ready", "pod", "-l", "app=webservice", "--timeout=300s") + k.Stdout = &output + k.Stderr = os.Stderr + err := k.Run() + if err != nil { + log.Panicf("failed to execute kubectl wait for gitlab pods with label app=webservice: %s \n%s", output, err) + } + log.Printf("the output is: %s", output.String()) +} + +func createVaultConfiguredSecret() { + if !viper.GetBool("vault.configuredsecret") { + var output bytes.Buffer + // todo - https://github.com/bcreane/k8sutils/blob/master/utils.go + // kubectl create secret generic vault-configured --from-literal=isConfigured=true + // the purpose of this command is to let the vault-unseal Job running in kuberenetes know that external secrets store should be able to connect to the configured vault + k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "create", "secret", "generic", "vault-configured", "--from-literal=isConfigured=true") + k.Stdout = &output + k.Stderr = os.Stderr + err := k.Run() + if err != nil { + log.Panicf("failed to create secret for vault-configured: %s", err) + } + log.Println("the secret create output is: %s", output.String()) + + viper.Set("vault.configuredsecret", true) + viper.WriteConfig() + } else { + log.Println("vault secret already created") + } +} + +func getVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { + name := "vault-unseal-keys" + log.Printf("Reading secret %s\n", name) + secret, err := vaultSecretClient.Get(context.TODO(), name, metaV1.GetOptions{}) + + if err != nil { + panic(err.Error()) + } + + var jsonData map[string]interface{} + + for _, value := range secret.Data { + if err := json.Unmarshal(value, &jsonData); err != nil { + panic(err) + } + vaultRootToken = jsonData["root_token"].(string) + } + return vaultRootToken +} + +func getSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key string) string { + secret, err := k8sClient.Get(context.TODO(), secretName, metaV1.GetOptions{}) + if err != nil { + log.Println(fmt.Sprintf("error getting key: %s from secret: %s", key, secretName), err) + } + return string(secret.Data[key]) +} + +func waitForNamespaceandPods(namespace, podLabel string) { + if !viper.GetBool("create.softserve.ready") { + x := 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", namespace, "get", fmt.Sprintf("namespace/%s", namespace)) + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + if err != nil { + log.Println(fmt.Sprintf("waiting for %s namespace to create ", namespace)) + time.Sleep(10 * time.Second) + } else { + log.Println(fmt.Sprintf("namespace %s found, continuing", namespace)) + time.Sleep(10 * time.Second) + i = 51 + } + } + for i := 0; i < x; i++ { + kGetPods := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", namespace, "get", "pods", "-l", podLabel) + kGetPods.Stdout = os.Stdout + kGetPods.Stderr = os.Stderr + err := kGetPods.Run() + if err != nil { + log.Println(fmt.Sprintf("waiting for %s pods to create ", namespace)) + time.Sleep(10 * time.Second) + } else { + log.Println(fmt.Sprintf("%s pods found, continuing", namespace)) + time.Sleep(10 * time.Second) + break + } + } + viper.Set("create.softserve.ready", true) + viper.WriteConfig() + } else { + log.Println("soft-serve is ready, skipping") + } +} diff --git a/cmd/root.go b/cmd/root.go index f7de07662..5b9141ddd 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,15 +14,10 @@ var cfgFile string // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "kubefirst", - Short: "A brief description of your application", - Long: `A longer description that spans multiple lines and likely contains -examples and usage of using your application. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, - // Uncomment the following line if your bare application - // has an action associated with it: + Short: "kubefirst management cluster installer base command", + Long: `kubefirst management cluster installer provisions an + open source application delivery platform in under an hour. + checkout the docs at docs.kubefirst.com.`, Run: func(cmd *cobra.Command, args []string) { log.Println(viper.Get("name")) //! print value coming from ~/.flare --> ~/.kubefirst }, diff --git a/configs/kubefirstDirectory.go b/configs/kubefirstDirectory.go index ed747c16e..2942c66e5 100755 --- a/configs/kubefirstDirectory.go +++ b/configs/kubefirstDirectory.go @@ -15,6 +15,6 @@ func CheckKubefirstDir(home string) error { return fmt.Errorf(errorMsg) } - log.Printf("\".kubefirst\" file found: %s", k1sDir) + log.Printf("\".kubefirst\" directory found: %s", k1sDir) return nil } diff --git a/internal/argocd/argocd.go b/internal/argocd/argocd.go index d4ec8c8b7..fe487f187 100644 --- a/internal/argocd/argocd.go +++ b/internal/argocd/argocd.go @@ -14,7 +14,7 @@ import ( "os" "os/exec" "strings" - "syscall" + "time" ) // kSyncArgocdApplication request ArgoCD to manual sync an application. Expected parameters are the ArgoCD application @@ -125,16 +125,10 @@ func GetArgocdAuthToken(dryRun bool) string { log.Printf("[#99] Dry-run mode, GetArgocdAuthToken skipped.") return "nothing" } - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to argocd %s", err) - } - url := "https://localhost:8080/api/v1/session" + time.Sleep(15 * time.Second) + + url := fmt.Sprintf("%s/api/v1/session", viper.GetString("argocd.local.service")) payload := strings.NewReader(fmt.Sprintf("{\n\t\"username\":\"admin\",\"password\":\"%s\"\n}", viper.GetString("argocd.admin.password"))) @@ -151,53 +145,50 @@ func GetArgocdAuthToken(dryRun bool) string { }, } - res, err := client.Do(req) - if err != nil { - log.Fatal("error requesting auth token from argocd") - } - - defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Fatal("error sending POST request to get argocd auth token :", err) - } - - var dat map[string]interface{} - - if err := json.Unmarshal(body, &dat); err != nil { - log.Panicf("error unmarshalling %s", err) - } - token := dat["token"] - viper.Set("argocd.admin.apitoken", token) - viper.WriteConfig() - - return token.(string) - + x := 3 + for i := 0; i < x; i++ { + res, err := client.Do(req) + if err != nil { + log.Panic("error requesting auth token from argocd", err) + } else { + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Panic("error sending POST request to get argocd auth token :", err) + } + + var dat map[string]interface{} + + if err := json.Unmarshal(body, &dat); err != nil { + log.Panicf("error unmarshalling %s", err) + } + token := dat["token"] + viper.Set("argocd.admin.apitoken", token) + viper.WriteConfig() + + // todo clean this up later + return token.(string) + } + } + return "" } func SyncArgocdApplication(dryRun bool, applicationName, argocdAuthToken string) { - config := configs.ReadConfig() if dryRun { log.Printf("[#99] Dry-run mode, SyncArgocdApplication skipped.") return } - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to argocd %s", err) - } // todo need to replace this with a curl wrapper and see if it WORKS url := fmt.Sprintf("https://localhost:8080/api/v1/applications/%s/sync", applicationName) + var outb bytes.Buffer argoCdAppSync := exec.Command("curl", "-k", "-L", "-X", "POST", url, "-H", fmt.Sprintf("Authorization: Bearer %s", argocdAuthToken)) argoCdAppSync.Stdout = os.Stdout argoCdAppSync.Stderr = os.Stderr - err = argoCdAppSync.Run() + err := argoCdAppSync.Run() + log.Println("the value from the curl command to sync registry in argocd is:", outb.String()) if err != nil { log.Panicf("error: curl appSync failed failed %s", err) } diff --git a/internal/aws/aws.go b/internal/aws/aws.go index 7c231ef7a..3dccf49a1 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -234,19 +234,19 @@ func GetDNSInfo(hostedZoneName string) string { log.Println("oh no error on call", err) } - var zoneId string + var hostedZoneId string for _, zone := range hostedZones.HostedZones { if *zone.Name == fmt.Sprintf(`%s%s`, hostedZoneName, ".") { - zoneId = ReturnHostedZoneId(*zone.Id) - log.Printf(`found entry for user submitted domain %s, using hosted zone id %s`, hostedZoneName, zoneId) + hostedZoneId = returnHostedZoneId(*zone.Id) + log.Printf(`found entry for user submitted domain %s, using hosted zone id %s`, hostedZoneName, hostedZoneId) viper.Set("aws.hostedzonename", hostedZoneName) - viper.Set("aws.domainid", zoneId) + viper.Set("aws.hostedzoneid", hostedZoneId) viper.WriteConfig() } } log.Println("GetDNSInfo (done)") - return zoneId + return hostedZoneId } diff --git a/internal/downloadManager/download.go b/internal/downloadManager/download.go index cdd2aef7d..e4b43ef35 100644 --- a/internal/downloadManager/download.go +++ b/internal/downloadManager/download.go @@ -86,6 +86,7 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke if err != nil { return err } + os.RemoveAll(fmt.Sprintf("%s/terraform.zip", toolsDir)) trackers[pkg.TrackerStage5].Tracker.Increment(int64(1)) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index f37a0b363..37f636d9a 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -31,7 +31,6 @@ import ( "os" "os/exec" "strings" - "syscall" "time" "golang.org/x/crypto/ssh" @@ -65,15 +64,6 @@ func GenerateKey() (string, string, error) { func GitlabGeneratePersonalAccessToken(gitlabPodName string) { config := configs.ReadConfig() - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to gitlab %s", err) - } - log.Println("generating gitlab personal access token on pod: ", gitlabPodName) id := uuid.New() @@ -82,7 +72,7 @@ func GitlabGeneratePersonalAccessToken(gitlabPodName string) { k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) k.Stdout = os.Stdout k.Stderr = os.Stderr - err = k.Run() + err := k.Run() if err != nil { log.Panicf("error running exec against %s to generate gitlab personal access token for root user", gitlabPodName) } @@ -250,35 +240,48 @@ func ProduceGitlabTokens(dryRun bool) { } func ApplyGitlabTerraform(dryRun bool, directory string) { + config := configs.ReadConfig() + if !viper.GetBool("create.terraformapplied.gitlab") { - log.Println("Executing ApplyGitlabTerraform") + log.Println("Executing applyGitlabTerraform") if dryRun { - log.Printf("[#99] Dry-run mode, ApplyGitlabTerraform skipped.") + log.Printf("[#99] Dry-run mode, applyGitlabTerraform skipped.") return } + //* AWS_SDK_LOAD_CONFIG=1 + //* https://registry.terraform.io/providers/hashicorp/aws/2.34.0/docs#shared-credentials-file + os.Setenv("AWS_SDK_LOAD_CONFIG", "1") + os.Setenv("AWS_PROFILE", "starter") // todo this is an issue // Prepare for terraform gitlab execution os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) - os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") + os.Setenv("GITLAB_BASE_URL", viper.GetString("gitlab.local.service")) directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) err := os.Chdir(directory) if err != nil { log.Panic("error: could not change directory to " + directory) } - _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") - if errInit != nil { - log.Panic(fmt.Sprintf("error: terraform init for gitlab failed %s", err)) + tfInitCmd := exec.Command(config.TerraformPath, "init") + tfInitCmd.Stdout = os.Stdout + tfInitCmd.Stderr = os.Stderr + err = tfInitCmd.Run() + if err != nil { + log.Panicf("error: terraform init for gitlab failed %s", err) } - _, _, errApply := pkg.ExecShellReturnStrings(config.TerraformPath, "apply", "-auto-approve") - if errApply != nil { - log.Panic(fmt.Sprintf("error: terraform apply for gitlab failed %s", err)) + + tfApplyCmd := exec.Command(config.TerraformPath, "apply", "-auto-approve") + tfApplyCmd.Stdout = os.Stdout + tfApplyCmd.Stderr = os.Stderr + err = tfApplyCmd.Run() + if err != nil { + log.Panicf("error: terraform apply for gitlab failed %s", err) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) viper.Set("create.terraformapplied.gitlab", true) viper.WriteConfig() } else { - log.Println("Skipping: ApplyGitlabTerraform") + log.Println("Skipping: applyGitlabTerraform") } } @@ -292,6 +295,10 @@ func GitlabKeyUpload(dryRun bool) { log.Printf("[#99] Dry-run mode, GitlabKeyUpload skipped.") return } + + os.Setenv("AWS_SDK_LOAD_CONFIG", "1") + os.Setenv("AWS_PROFILE", "starter") // todo this is an issue + log.Println("uploading ssh public key to gitlab") gitlabToken := viper.GetString("gitlab.token") data := url.Values{ @@ -299,7 +306,9 @@ func GitlabKeyUpload(dryRun bool) { "key": {viper.GetString("botpublickey")}, } - gitlabUrlBase := fmt.Sprintf("https://gitlab.%s", viper.GetString("aws.domainname")) + time.Sleep(10 * time.Second) // todo, build in a retry + + gitlabUrlBase := viper.GetString("gitlab.local.service") resp, err := http.PostForm(gitlabUrlBase+"/api/v4/user/keys?private_token="+gitlabToken, data) if err != nil { @@ -319,16 +328,6 @@ func GitlabKeyUpload(dryRun bool) { func DestroyGitlabTerraform(skipGitlabTerraform bool) { config := configs.ReadConfig() - log.Println("\n\nTODO -- need to setup and argocd delete against registry and wait?\n\n") - // kubeconfig := os.Getenv("HOME") + "/.kube/config" - // config, err := argocdclientset.BuildConfigFromFlags("", kubeconfig) - // argocdclientset, err := argocdclientset.NewForConfig(config) - // if err != nil { - // return nil, err - // } - - //* should we git clone the gitops repo when destroy is run back to their - //* local host to get the latest values of gitops os.Setenv("AWS_REGION", viper.GetString("aws.region")) os.Setenv("AWS_ACCOUNT_ID", viper.GetString("aws.accountid")) @@ -345,7 +344,7 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { log.Panicf("error: could not change directory to " + directory) } - os.Setenv("GITLAB_BASE_URL", "http://localhost:8888") + os.Setenv("GITLAB_BASE_URL", viper.GetString("gitlab.local.service")) if !skipGitlabTerraform { tfInitGitlabCmd := exec.Command(config.TerraformPath, "init") @@ -540,3 +539,70 @@ func HydrateGitlabMetaphorRepo(dryRun bool) { } } + +// refactor: review it +func PushGitRepo(gitOrigin, repoName string) { + + repoDir := fmt.Sprintf("%s/.kubefirst/%s", home, repoName) + repo, err := git.PlainOpen(repoDir) + if err != nil { + log.Panicf("error opening repo %s: %s", repoName, err) + } + + // todo - fix opts := &git.PushOptions{uniqe, stuff} .Push(opts) ? + if gitOrigin == "soft" { + detokenize(repoDir) + os.RemoveAll(repoDir + "/terraform/base/.terraform") + os.RemoveAll(repoDir + "/terraform/gitlab/.terraform") + os.RemoveAll(repoDir + "/terraform/vault/.terraform") + os.Remove(repoDir + "/terraform/base/.terraform.lock.hcl") + os.Remove(repoDir + "/terraform/gitlab/.terraform.lock.hcl") + commitToRepo(repo, repoName) + auth, _ := publicKey() + + auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + + err = repo.Push(&git.PushOptions{ + RemoteName: gitOrigin, + Auth: auth, + }) + if err != nil { + log.Panicf("error pushing detokenized %s repository to remote at %s", repoName, gitOrigin) + } + log.Printf("successfully pushed %s to soft-serve", repoName) + } + + if gitOrigin == "gitlab" { + + auth := &http.BasicAuth{ + Username: "root", + Password: viper.GetString("gitlab.token"), + } + err = repo.Push(&git.PushOptions{ + RemoteName: gitOrigin, + Auth: auth, + }) + if err != nil { + log.Panicf("error pushing detokenized %s repository to remote at %s", repoName, gitOrigin) + } + log.Printf("successfully pushed %s to gitlab", repoName) + } + + viper.Set(fmt.Sprintf("create.repos.%s.%s.pushed", gitOrigin, repoName), true) + viper.WriteConfig() +} + +// refactor: review it +func CommitToRepo(repo *git.Repository, repoName string) { + w, _ := repo.Worktree() + + log.Println(fmt.Sprintf("committing detokenized %s kms key id", repoName)) + w.Add(".") + w.Commit(fmt.Sprintf("committing detokenized %s kms key id", repoName), &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: "kubefirst-bot@kubefirst.com", + When: time.Now(), + }, + }) +} \ No newline at end of file diff --git a/internal/softserve/softserve.go b/internal/softserve/softserve.go index 5bfa04d64..826e9ba3c 100644 --- a/internal/softserve/softserve.go +++ b/internal/softserve/softserve.go @@ -5,79 +5,61 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/gitClient" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" ssh2 "golang.org/x/crypto/ssh" "io/ioutil" "log" - "os" - "os/exec" "strings" - "syscall" "time" ) func CreateSoftServe(dryRun bool, kubeconfigPath string) { config := configs.ReadConfig() if !viper.GetBool("create.softserve.create") { - log.Println("Executing CreateSoftServe") + log.Println("creating soft-serve") if dryRun { - log.Printf("[#99] Dry-run mode, CreateSoftServe skipped.") + log.Printf("[#99] Dry-run mode, createSoftServe skipped.") return } - toolsDir := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) - err := os.Mkdir(toolsDir, 0777) - if err != nil { - log.Println("error creating directory %s", toolsDir, err) - } - - // create soft-serve stateful set softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", config.HomePath) softServeApplyOut, softServeApplyErr, errSoftServeApply := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") log.Printf("Result:\n\t%s\n\t%s\n", softServeApplyOut, softServeApplyErr) if errSoftServeApply != nil { - log.Panicf("failed to call kubectlCreateSoftServeCmd.Run(): %v", err) + log.Panicf("error: failed to apply soft-serve to the cluster %s", errSoftServeApply) } viper.Set("create.softserve.create", true) viper.WriteConfig() - log.Println("waiting for soft-serve installation to complete...") - time.Sleep(60 * time.Second) - //TODO: Update mechanism of waiting + } else { - log.Println("Skipping: CreateSoftServe") + log.Println("Skipping: createSoftServe") } } func ConfigureSoftServeAndPush(dryRun bool) { config := configs.ReadConfig() + configureAndPushFlag := viper.GetBool("create.softserve.configure") - if configureAndPushFlag != true { - log.Println("Executing ConfigureSoftServeAndPush") + + if !configureAndPushFlag { + log.Println("Executing configureSoftserveAndPush") if dryRun { - log.Printf("[#99] Dry-run mode, ConfigureSoftServeAndPush skipped.") + log.Printf("[#99] Dry-run mode, configureSoftserveAndPush skipped.") return } - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to soft-serve %s", err) - } - time.Sleep(20 * time.Second) configureSoftServe() - gitClient.PushGitopsToSoftServe() + // refactor: update it + PushGitRepo("soft", "gitops") + viper.Set("create.softserve.configure", true) viper.WriteConfig() time.Sleep(30 * time.Second) } else { - log.Println("Skipping: ConfigureSoftServeAndPush") + log.Println("Skipping: configureSoftserveAndPush") } } diff --git a/internal/vault/vault.go b/internal/vault/vault.go index f65ba0777..ed31ec478 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -89,6 +89,9 @@ func ConfigureVault(dryRun bool) { // Prepare for terraform vault execution os.Setenv("VAULT_ADDR", "http://localhost:8200") os.Setenv("VAULT_TOKEN", vaultToken) + os.Setenv("AWS_SDK_LOAD_CONFIG", "1") + os.Setenv("AWS_PROFILE", "starter") // todo this is an issue + os.Setenv("AWS_DEFAULT_REGION", viper.GetString("aws.region")) os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename"))) os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) @@ -140,7 +143,7 @@ func AddGitlabOidcApplications(dryRun bool) { domain := viper.GetString("aws.hostedzonename") git, err := gitlab.NewClient( viper.GetString("gitlab.token"), - gitlab.WithBaseURL(fmt.Sprintf("https://gitlab.%s/api/v4", domain)), + gitlab.WithBaseURL(fmt.Sprintf("%s/api/v4", viper.GetString("gitlab.local.service"))), ) if err != nil { log.Fatal(err) diff --git a/pkg/helpers.go b/pkg/helpers.go index f07478969..7106ab327 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -55,8 +55,8 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { } botPublicKey := viper.GetString("botpublickey") - domainId := viper.GetString("aws.domainid") - hostedzonename := viper.GetString("aws.hostedzonename") + hostedZoneId := viper.GetString("aws.hostedzoneid") + hostedZoneName := viper.GetString("aws.hostedzonename") bucketStateStore := viper.GetString("bucket.state-store.name") bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") diff --git a/pkg/keys.go b/pkg/keys.go index 8011c497c..7fbb024a2 100644 --- a/pkg/keys.go +++ b/pkg/keys.go @@ -107,3 +107,18 @@ func GenerateKey() (string, string, error) { return publicKey, privateKey, nil } + +func ModConfigYaml() { + + file, err := ioutil.ReadFile("./config.yaml") + if err != nil { + log.Println("error reading file", err) + } + + newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) + + err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) + if err != nil { + panic(err) + } +} diff --git a/pkg/progress_bar.go b/pkg/progress_bar.go index b3ba22994..d11049442 100644 --- a/pkg/progress_bar.go +++ b/pkg/progress_bar.go @@ -40,7 +40,9 @@ var ( flagHidePercentage = flag.Bool("hide-percentage", false, "Hide the progress percent?") flagHideTime = flag.Bool("hide-time", false, "Hide the time taken?") flagHideValue = flag.Bool("hide-value", false, "Hide the tracker value?") -) + // flagNumTrackers = flag.Int("num-trackers", 12, "Number of Trackers") + flagRandomFail = flag.Bool("rnd-fail", false, "Introduce random failures in tracking") + flagRandomLogs = flag.Bool("rnd-logs", false, "Output random logs in the middle of tracking") // GetTrackers keeps one single instance of Trackers alive using singleton pattern. func GetTrackers() map[string]*ActionTracker { From 96869c7a2600801c52dab80832475b4c260e357f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Mon, 11 Jul 2022 15:35:50 -0300 Subject: [PATCH 057/107] chore: merge in progress, code is breaking at init command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/argocdSync.go | 12 ++- cmd/create.go | 42 +++++---- cmd/destroy.go | 28 +----- cmd/init.go | 17 ++-- cmd/kubefirstTemplate.go | 156 ++++++++++++++++++++++++++++++++ cmd/kubernetes.go | 19 ++-- internal/argocd/argocd.go | 2 - internal/aws/aws.go | 2 +- internal/gitlab/gitlab.go | 16 ++-- internal/k8s/kubernetes.go | 25 ++--- internal/softserve/softserve.go | 3 +- pkg/helpers.go | 6 +- pkg/progress_bar.go | 1 + 13 files changed, 235 insertions(+), 94 deletions(-) create mode 100644 cmd/kubefirstTemplate.go diff --git a/cmd/argocdSync.go b/cmd/argocdSync.go index bc972d672..bc1a7a732 100644 --- a/cmd/argocdSync.go +++ b/cmd/argocdSync.go @@ -1,6 +1,7 @@ package cmd import ( + "github.com/kubefirst/nebulous/internal/argocd" "log" "github.com/spf13/cobra" @@ -19,6 +20,13 @@ This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { + dryRun, err := cmd.Flags().GetBool("dry-run") + if err != nil { + log.Panic(err) + } + + log.Println("dry run enabled:", dryRun) + applicationName, _ := cmd.Flags().GetString("app-name") refreshToken, _ := cmd.Flags().GetBool("refresh-token") @@ -28,10 +36,10 @@ to quickly create a Cobra application.`, log.Panic("uh oh - no argocd auth token found in config, try again with `--refresh-token` ") } else { log.Println("getting a new argocd session token") - authToken = getArgocdAuthToken() + authToken = argocd.GetArgocdAuthToken(dryRun) } log.Printf("syncing the %s application", applicationName) - syncArgocdApplication(applicationName, authToken) + argocd.SyncArgocdApplication(dryRun, applicationName, authToken) }, } diff --git a/cmd/create.go b/cmd/create.go index 0c9a4a423..5fc55b8c8 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/argocd" "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/helm" "github.com/kubefirst/nebulous/internal/softserve" @@ -16,6 +17,7 @@ import ( "k8s.io/client-go/tools/clientcmd" "log" "os" + "os/exec" "syscall" "time" ) @@ -78,15 +80,15 @@ to quickly create a Cobra application.`, //! soft-serve was just applied softserve.CreateSoftServe(dryRun, config.KubeConfigPath) - waitForNamespaceandPods("soft-serve", "app=soft-serve") + waitForNamespaceandPods(config, "soft-serve", "app=soft-serve") // todo this should be replaced with something more intelligent log.Println("waiting for soft-serve installation to complete...") time.Sleep(60 * time.Second) - kPortForwardSoftServe := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") + kPortForwardSoftServe := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") kPortForwardSoftServe.Stdout = os.Stdout kPortForwardSoftServe.Stderr = os.Stderr - err := kPortForwardSoftServe.Start() + err = kPortForwardSoftServe.Start() defer kPortForwardSoftServe.Process.Signal(syscall.SIGTERM) if err != nil { log.Panicf("error: failed to port-forward to soft-serve %s", err) @@ -102,7 +104,7 @@ to quickly create a Cobra application.`, //! argocd was just helm installed x := 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "get", "namespace/argocd") + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/argocd") kGetNamespace.Stdout = os.Stdout kGetNamespace.Stderr = os.Stderr err := kGetNamespace.Run() @@ -116,7 +118,7 @@ to quickly create a Cobra application.`, } } for i := 0; i < x; i++ { - kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "get", "pods", "-l", "app.kubernetes.io/name=argocd-server") + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "pods", "-l", "app.kubernetes.io/name=argocd-server") kGetNamespace.Stdout = os.Stdout kGetNamespace.Stderr = os.Stderr err := kGetNamespace.Run() @@ -130,7 +132,7 @@ to quickly create a Cobra application.`, } } - kPortForwardArgocd := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") + kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") kPortForwardArgocd.Stdout = os.Stdout kPortForwardArgocd.Stderr = os.Stderr err = kPortForwardArgocd.Start() @@ -145,9 +147,9 @@ to quickly create a Cobra application.`, log.Println("setting argocd credentials") setArgocdCreds() log.Println("getting an argocd auth token") - token := getArgocdAuthToken() + token := argocd.GetArgocdAuthToken(dryRun) log.Println("syncing the registry application") - syncArgocdApplication("registry", token) + argocd.SyncArgocdApplication(dryRun, "registry", token) // todo, need to stall until the registry has synced, then get to ui asap //! skip this if syncing from argocd and not helm installing @@ -159,7 +161,7 @@ to quickly create a Cobra application.`, //! x = 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "get", "namespace/vault") + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/vault") kGetNamespace.Stdout = os.Stdout kGetNamespace.Stderr = os.Stderr err := kGetNamespace.Run() @@ -174,7 +176,7 @@ to quickly create a Cobra application.`, } x = 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "get", "pods", "-l", "vault-initialized=true") + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "get", "pods", "-l", "vault-initialized=true") kGetNamespace.Stdout = os.Stdout kGetNamespace.Stderr = os.Stderr err := kGetNamespace.Run() @@ -187,7 +189,7 @@ to quickly create a Cobra application.`, break } } - kPortForwardVault := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") kPortForwardVault.Stdout = os.Stdout kPortForwardVault.Stderr = os.Stderr err = kPortForwardVault.Start() @@ -198,7 +200,7 @@ to quickly create a Cobra application.`, x = 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "get", "namespace/gitlab") + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/gitlab") kGetNamespace.Stdout = os.Stdout kGetNamespace.Stderr = os.Stderr err := kGetNamespace.Run() @@ -213,7 +215,7 @@ to quickly create a Cobra application.`, } x = 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "get", "pods", "-l", "app=webservice") + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "get", "pods", "-l", "app=webservice") kGetNamespace.Stdout = os.Stdout kGetNamespace.Stderr = os.Stderr err := kGetNamespace.Run() @@ -227,9 +229,9 @@ to quickly create a Cobra application.`, } } log.Println("waiting for gitlab") - waitForGitlab() + waitForGitlab(config) log.Println("gitlab is ready!") - kPortForwardGitlab := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + kPortForwardGitlab := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") kPortForwardGitlab.Stdout = os.Stdout kPortForwardGitlab.Stderr = os.Stderr err = kPortForwardGitlab.Start() @@ -251,7 +253,7 @@ to quickly create a Cobra application.`, if !skipVault { log.Println("waiting for vault unseal") - waitForVaultUnseal() + waitForVaultUnseal(config) log.Println("vault unseal condition met - continuing") log.Println("configuring vault") @@ -259,7 +261,7 @@ to quickly create a Cobra application.`, log.Println("vault configured") log.Println("creating vault configured secret") - createVaultConfiguredSecret() + createVaultConfiguredSecret(config) log.Println("vault-configured secret created") Trackers[trackerStage23].Tracker.Increment(int64(1)) @@ -272,7 +274,8 @@ to quickly create a Cobra application.`, log.Println("pushing gitops repo to origin gitlab") // refactor: sounds like a new functions, should PushGitOpsToGitLab be renamed/update signature? - pushGitRepo("gitlab", "gitops") // todo need to handle if this was already pushed, errors on failure + + gitlab.PushGitRepo(config, "gitlab", "gitops") // todo: need to handle if this was already pushed, errors on failure) gitlab.PushGitOpsToGitLab(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) @@ -321,7 +324,8 @@ func init() { // todo: move it to internals/ArgoCD func setArgocdCreds() { - config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + cfg := configs.ReadConfig() + config, err := clientcmd.BuildConfigFromFlags("", cfg.KubeConfigPath) if err != nil { panic(err.Error()) } diff --git a/cmd/destroy.go b/cmd/destroy.go index 38cb40ff8..31d8788d9 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -1,19 +1,16 @@ package cmd import ( - "fmt" "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/internal/aws" "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/internal/k8s" "github.com/kubefirst/nebulous/internal/terraform" "github.com/spf13/cobra" - "github.com/spf13/viper" "log" "os" "os/exec" "syscall" - "time" ) // destroyCmd represents the destroy command @@ -55,7 +52,7 @@ if the registry has already been deleted.`, log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) } - kPortForwardArgocd := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") + kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") kPortForwardArgocd.Stdout = os.Stdout kPortForwardArgocd.Stderr = os.Stderr err = kPortForwardArgocd.Start() @@ -63,7 +60,7 @@ if the registry has already been deleted.`, if err != nil { log.Panicf("error: failed to port-forward to argocd in main thread %s", err) } - kPortForwardVault := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") kPortForwardVault.Stdout = os.Stdout kPortForwardVault.Stderr = os.Stderr err = kPortForwardVault.Start() @@ -97,24 +94,3 @@ func init() { destroyCmd.Flags().Bool("skip-base-terraform", false, "whether to skip the terraform destroy against base install - note: if you already deleted registry it doesnt exist") destroyCmd.Flags().Bool("destroy-buckets", false, "remove created aws buckets, not empty buckets are not cleaned") } - -func deleteArgocdRegistryApplication() { - if !skipDeleteRegistryApplication { - - log.Println("refreshing argocd session token") - getArgocdAuthToken() - - url := "https://localhost:8080/api/v1/applications/registry" - argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) - argoCdAppSync.Stdout = os.Stdout - argoCdAppSync.Stderr = os.Stderr - err := argoCdAppSync.Run() - if err != nil { - log.Panicf("error: delete registry applicatoin from argocd failed: %s", err) - } - log.Println("waiting for argocd deletion to complete") - time.Sleep(300 * time.Second) - } else { - log.Println("skip: deleteRegistryApplication") - } -} diff --git a/cmd/init.go b/cmd/init.go index a9b238cb6..4fd8d6269 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -90,15 +90,16 @@ to quickly create a Cobra application.`, // refactor: confirm it (start) //! tracker 0 log.Println("installing kubefirst dependencies") - download() + + downloadManager.DownloadTools(config, trackers) log.Println("dependency installation complete") - Trackers[trackerStage0].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage0].Tracker.Increment(int64(1)) //! tracker 1 log.Println("getting aws account information") - getAccountInfo() + aws.GetAccountInfo() log.Printf("aws account id: %s\naws user arn: %s", viper.GetString("aws.accountid"), viper.GetString("aws.userarn")) - Trackers[trackerStage1].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage1].Tracker.Increment(int64(1)) // refactor: confirm it (end) // hosted zone id @@ -184,14 +185,14 @@ to quickly create a Cobra application.`, } //! tracker 6 - prepareKubefirstTemplateRepo(gitopsTemplateGithubOrgOverride, "gitops") + prepareKubefirstTemplateRepo(config, gitopsTemplateGithubOrgOverride, "gitops") log.Println("clone and detokenization of gitops-template repository complete") - Trackers[trackerStage6].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage6].Tracker.Increment(int64(1)) //! tracker 7 log.Printf("cloning and detokenizing the metaphor-template repository") - prepareKubefirstTemplateRepo("kubefirst", "metaphor") + prepareKubefirstTemplateRepo(config, "kubefirst", "metaphor") log.Println("clone and detokenization of metaphor-template repository complete") - Trackers[trackerStage7].Tracker.Increment(int64(1)) + trackers[pkg.TrackerStage7].Tracker.Increment(int64(1)) metricName = "kubefirst.init.completed" diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go new file mode 100644 index 000000000..8a8ce857b --- /dev/null +++ b/cmd/kubefirstTemplate.go @@ -0,0 +1,156 @@ +package cmd + +import ( + "fmt" + "github.com/kubefirst/nebulous/configs" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + "time" + + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/spf13/viper" +) + +func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName string) { + + repoUrl := fmt.Sprintf("https://github.com/%s/%s-template", githubOrg, repoName) + directory := fmt.Sprintf("%s/.kubefirst/%s", config.HomePath, repoName) + log.Println("git clone", repoUrl, directory) + + repo, err := git.PlainClone(directory, false, &git.CloneOptions{ + URL: repoUrl, + }) + if err != nil { + log.Panicf("error cloning %s-template repository from github %s", repoName, err) + } + viper.Set(fmt.Sprintf("init.repos.%s.cloned", repoName), true) + viper.WriteConfig() + + log.Printf("cloned %s-template repository to directory %s/.kubefirst/%s", repoName, config.HomePath, repoName) + + log.Printf("detokenizing %s/.kubefirst/%s", config.HomePath, repoName) + detokenize(directory) + log.Printf("detokenization of %s/.kubefirst/%s complete", config.HomePath, repoName) + + viper.Set(fmt.Sprintf("init.repos.%s.detokenized", repoName), true) + viper.WriteConfig() + + domain := viper.GetString("aws.hostedzonename") + log.Printf("creating git remote gitlab") + log.Println("git remote add gitlab at url ", fmt.Sprintf("https://gitlab.%s/kubefirst/%s.git", domain, repoName)) + _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "gitlab", + URLs: []string{fmt.Sprintf("https://gitlab.%s/kubefirst/%s.git", domain, repoName)}, + }) + + if repoName == "gitops" { + log.Println("creating git remote ssh://127.0.0.1:8022/gitops") + _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "soft", + URLs: []string{"ssh://127.0.0.1:8022/gitops"}, + }) + } + + w, _ := repo.Worktree() + + log.Println(fmt.Sprintf("committing detokenized %s content", repoName)) + w.Add(".") + w.Commit(fmt.Sprintf("committing detokenized %s content", repoName), &git.CommitOptions{ + Author: &object.Signature{ + Name: "kubefirst-bot", + Email: "kubefirst-bot@kubefirst.com", + When: time.Now(), + }, + }) + viper.WriteConfig() +} + +func detokenize(path string) { + + err := filepath.Walk(path, detokenizeDirectory) + if err != nil { + panic(err) + } +} + +func detokenizeDirectory(path string, fi os.FileInfo, err error) error { + if err != nil { + return err + } + + if fi.IsDir() { + return nil // + } + + if strings.Contains(path, ".git/") || strings.Contains(path, ".terraform") { + return nil + } + + matched, err := filepath.Match("*", fi.Name()) + + if err != nil { + panic(err) + } + + if matched { + read, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + // todo should detokenize be a switch statement based on a value found in viper? + gitlabConfigured := viper.GetBool("gitlab.keyuploaded") + + newContents := "" + + if gitlabConfigured { + newContents = strings.Replace(string(read), "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")), -1) + } else { + newContents = strings.Replace(string(read), "https://gitlab./kubefirst/gitops.git", "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", -1) + } + + botPublicKey := viper.GetString("botpublickey") + hostedZoneId := viper.GetString("aws.hostedzoneid") + hostedZoneName := viper.GetString("aws.hostedzonename") + bucketStateStore := viper.GetString("bucket.state-store.name") + bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") + bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") + bucketChartmuseum := viper.GetString("bucket.chartmuseum.name") + region := viper.GetString("aws.region") + adminEmail := viper.GetString("adminemail") + awsAccountId := viper.GetString("aws.accountid") + kmsKeyId := viper.GetString("vault.kmskeyid") + + newContents = strings.Replace(newContents, "", strings.TrimSpace(botPublicKey), -1) + newContents = strings.Replace(newContents, "", bucketStateStore, -1) + newContents = strings.Replace(newContents, "", bucketArgoArtifacts, -1) + newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) + newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) + newContents = strings.Replace(newContents, "", hostedZoneId, -1) + newContents = strings.Replace(newContents, "", hostedZoneName, -1) + newContents = strings.Replace(newContents, "", region, -1) + newContents = strings.Replace(newContents, "", adminEmail, -1) + newContents = strings.Replace(newContents, "", awsAccountId, -1) + if kmsKeyId != "" { + newContents = strings.Replace(newContents, "", kmsKeyId, -1) + } + + if viper.GetBool("create.terraformapplied.gitlab") { + newContents = strings.Replace(newContents, "", hostedZoneName, -1) + newContents = strings.Replace(newContents, "", region, -1) + newContents = strings.Replace(newContents, "", awsAccountId, -1) + } + + err = ioutil.WriteFile(path, []byte(newContents), 0) + if err != nil { + panic(err) + } + + } + + return nil +} diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index d98256ad2..6eb1c00ed 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -9,6 +9,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/kubefirst/nebulous/configs" "log" "os" "os/exec" @@ -39,13 +40,13 @@ func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) return gitlabToolboxPodName } -func waitForVaultUnseal() { +func waitForVaultUnseal(config *configs.Config) { vaultReady := viper.GetBool("create.vault.ready") if !vaultReady { var output bytes.Buffer // todo - add a viper.GetBool() check to the beginning of this function // todo write in golang? see here -> https://github.com/bcreane/k8sutils/blob/master/utils.go - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "wait", "--for=condition=ready", "pod", "-l", "vault-initialized=true", "--timeout=300s") + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "wait", "--for=condition=ready", "pod", "-l", "vault-initialized=true", "--timeout=300s") k.Stdout = &output k.Stderr = os.Stderr err := k.Run() @@ -59,11 +60,11 @@ func waitForVaultUnseal() { } -func waitForGitlab() { +func waitForGitlab(config *configs.Config) { var output bytes.Buffer // todo - add a viper.GetBool() check to the beginning of this function // todo write in golang? see here -> https://github.com/bcreane/k8sutils/blob/master/utils.go - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "gitlab", "wait", "--for=condition=ready", "pod", "-l", "app=webservice", "--timeout=300s") + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "wait", "--for=condition=ready", "pod", "-l", "app=webservice", "--timeout=300s") k.Stdout = &output k.Stderr = os.Stderr err := k.Run() @@ -73,13 +74,13 @@ func waitForGitlab() { log.Printf("the output is: %s", output.String()) } -func createVaultConfiguredSecret() { +func createVaultConfiguredSecret(config *configs.Config) { if !viper.GetBool("vault.configuredsecret") { var output bytes.Buffer // todo - https://github.com/bcreane/k8sutils/blob/master/utils.go // kubectl create secret generic vault-configured --from-literal=isConfigured=true // the purpose of this command is to let the vault-unseal Job running in kuberenetes know that external secrets store should be able to connect to the configured vault - k := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "vault", "create", "secret", "generic", "vault-configured", "--from-literal=isConfigured=true") + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "create", "secret", "generic", "vault-configured", "--from-literal=isConfigured=true") k.Stdout = &output k.Stderr = os.Stderr err := k.Run() @@ -123,11 +124,11 @@ func getSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key strin return string(secret.Data[key]) } -func waitForNamespaceandPods(namespace, podLabel string) { +func waitForNamespaceandPods(config *configs.Config, namespace, podLabel string) { if !viper.GetBool("create.softserve.ready") { x := 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", namespace, "get", fmt.Sprintf("namespace/%s", namespace)) + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", namespace, "get", fmt.Sprintf("namespace/%s", namespace)) kGetNamespace.Stdout = os.Stdout kGetNamespace.Stderr = os.Stderr err := kGetNamespace.Run() @@ -141,7 +142,7 @@ func waitForNamespaceandPods(namespace, podLabel string) { } } for i := 0; i < x; i++ { - kGetPods := exec.Command(kubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", namespace, "get", "pods", "-l", podLabel) + kGetPods := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", namespace, "get", "pods", "-l", podLabel) kGetPods.Stdout = os.Stdout kGetPods.Stderr = os.Stderr err := kGetPods.Run() diff --git a/internal/argocd/argocd.go b/internal/argocd/argocd.go index fe487f187..82a07bb56 100644 --- a/internal/argocd/argocd.go +++ b/internal/argocd/argocd.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/kubefirst/nebulous/configs" "github.com/spf13/viper" "io/ioutil" "log" @@ -119,7 +118,6 @@ func getArgoCDToken(username string, password string) (string, error) { } func GetArgocdAuthToken(dryRun bool) string { - config := configs.ReadConfig() if dryRun { log.Printf("[#99] Dry-run mode, GetArgocdAuthToken skipped.") diff --git a/internal/aws/aws.go b/internal/aws/aws.go index 3dccf49a1..36fac44c8 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -238,7 +238,7 @@ func GetDNSInfo(hostedZoneName string) string { for _, zone := range hostedZones.HostedZones { if *zone.Name == fmt.Sprintf(`%s%s`, hostedZoneName, ".") { - hostedZoneId = returnHostedZoneId(*zone.Id) + hostedZoneId = ReturnHostedZoneId(*zone.Id) log.Printf(`found entry for user submitted domain %s, using hosted zone id %s`, hostedZoneName, hostedZoneId) viper.Set("aws.hostedzonename", hostedZoneName) viper.Set("aws.hostedzoneid", hostedZoneId) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 37f636d9a..096d8e317 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -541,9 +541,9 @@ func HydrateGitlabMetaphorRepo(dryRun bool) { } // refactor: review it -func PushGitRepo(gitOrigin, repoName string) { +func PushGitRepo(config *configs.Config, gitOrigin, repoName string) { - repoDir := fmt.Sprintf("%s/.kubefirst/%s", home, repoName) + repoDir := fmt.Sprintf("%s/.kubefirst/%s", config.HomePath, repoName) repo, err := git.PlainOpen(repoDir) if err != nil { log.Panicf("error opening repo %s: %s", repoName, err) @@ -551,16 +551,16 @@ func PushGitRepo(gitOrigin, repoName string) { // todo - fix opts := &git.PushOptions{uniqe, stuff} .Push(opts) ? if gitOrigin == "soft" { - detokenize(repoDir) + pkg.Detokenize(repoDir) os.RemoveAll(repoDir + "/terraform/base/.terraform") os.RemoveAll(repoDir + "/terraform/gitlab/.terraform") os.RemoveAll(repoDir + "/terraform/vault/.terraform") os.Remove(repoDir + "/terraform/base/.terraform.lock.hcl") os.Remove(repoDir + "/terraform/gitlab/.terraform.lock.hcl") - commitToRepo(repo, repoName) - auth, _ := publicKey() + CommitToRepo(repo, repoName) + auth, _ := pkg.PublicKey() - auth.HostKeyCallback = ssh2.InsecureIgnoreHostKey() + auth.HostKeyCallback = ssh.InsecureIgnoreHostKey() err = repo.Push(&git.PushOptions{ RemoteName: gitOrigin, @@ -574,7 +574,7 @@ func PushGitRepo(gitOrigin, repoName string) { if gitOrigin == "gitlab" { - auth := &http.BasicAuth{ + auth := &gitHttp.BasicAuth{ Username: "root", Password: viper.GetString("gitlab.token"), } @@ -605,4 +605,4 @@ func CommitToRepo(repo *git.Repository, repoName string) { When: time.Now(), }, }) -} \ No newline at end of file +} diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index 28a6939bd..e133a6214 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -8,14 +8,14 @@ import ( "context" "encoding/json" "fmt" - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/argocd" "github.com/spf13/viper" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" "log" "os" "os/exec" - "syscall" + "time" ) var vaultRootToken string @@ -67,27 +67,22 @@ func GetSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key strin } func DeleteRegistryApplication(skipDeleteRegistryApplication bool) { - config := configs.ReadConfig() + if !skipDeleteRegistryApplication { - log.Println("starting port forward to argocd server and deleting registry") - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr - err := kPortForward.Start() - defer kPortForward.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to argocd %s", err) - } + + log.Println("refreshing argocd session token") + argocd.GetArgocdAuthToken(false) url := "https://localhost:8080/api/v1/applications/registry" argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) argoCdAppSync.Stdout = os.Stdout argoCdAppSync.Stderr = os.Stderr - err = argoCdAppSync.Run() + err := argoCdAppSync.Run() if err != nil { - log.Panicf("error: curl appSync failed failed %s", err) + log.Panicf("error: delete registry applicatoin from argocd failed: %s", err) } - log.Println("deleting argocd application registry") + log.Println("waiting for argocd deletion to complete") + time.Sleep(300 * time.Second) } else { log.Println("skip: deleteRegistryApplication") } diff --git a/internal/softserve/softserve.go b/internal/softserve/softserve.go index 826e9ba3c..6dbec4724 100644 --- a/internal/softserve/softserve.go +++ b/internal/softserve/softserve.go @@ -5,6 +5,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/nebulous/internal/gitlab" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/viper" ssh2 "golang.org/x/crypto/ssh" @@ -53,7 +54,7 @@ func ConfigureSoftServeAndPush(dryRun bool) { configureSoftServe() // refactor: update it - PushGitRepo("soft", "gitops") + gitlab.PushGitRepo(config, "soft", "gitops") viper.Set("create.softserve.configure", true) viper.WriteConfig() diff --git a/pkg/helpers.go b/pkg/helpers.go index 7106ab327..02319b3c5 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -72,8 +72,8 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { newContents = strings.Replace(newContents, "", bucketArgoArtifacts, -1) newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) - newContents = strings.Replace(newContents, "", domainId, -1) - newContents = strings.Replace(newContents, "", hostedzonename, -1) + newContents = strings.Replace(newContents, "", hostedZoneId, -1) + newContents = strings.Replace(newContents, "", hostedZoneName, -1) newContents = strings.Replace(newContents, "", region, -1) newContents = strings.Replace(newContents, "", adminEmail, -1) newContents = strings.Replace(newContents, "", awsAccountId, -1) @@ -83,7 +83,7 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { newContents = strings.Replace(newContents, "", clusterName, -1) if viper.GetBool("create.terraformapplied.gitlab") { - newContents = strings.Replace(newContents, "", hostedzonename, -1) + newContents = strings.Replace(newContents, "", hostedZoneName, -1) newContents = strings.Replace(newContents, "", region, -1) newContents = strings.Replace(newContents, "", awsAccountId, -1) } diff --git a/pkg/progress_bar.go b/pkg/progress_bar.go index d11049442..aec7f22ca 100644 --- a/pkg/progress_bar.go +++ b/pkg/progress_bar.go @@ -43,6 +43,7 @@ var ( // flagNumTrackers = flag.Int("num-trackers", 12, "Number of Trackers") flagRandomFail = flag.Bool("rnd-fail", false, "Introduce random failures in tracking") flagRandomLogs = flag.Bool("rnd-logs", false, "Output random logs in the middle of tracking") +) // GetTrackers keeps one single instance of Trackers alive using singleton pattern. func GetTrackers() map[string]*ActionTracker { From b533dd6f831e784b31644a702a21184f491bdec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Mon, 11 Jul 2022 16:56:51 -0300 Subject: [PATCH 058/107] refactor: update progress bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/init.go | 149 ++++++++++++--------------- internal/aws/aws.go | 2 +- internal/downloadManager/download.go | 2 +- pkg/progress_bar.go | 34 +++--- 4 files changed, 89 insertions(+), 98 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 4fd8d6269..edd4eb5ea 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -9,7 +9,6 @@ import ( "github.com/kubefirst/nebulous/configs" "github.com/kubefirst/nebulous/internal/aws" "github.com/kubefirst/nebulous/internal/downloadManager" - "github.com/kubefirst/nebulous/internal/gitClient" "github.com/kubefirst/nebulous/internal/telemetry" "github.com/kubefirst/nebulous/pkg" "github.com/spf13/cobra" @@ -39,16 +38,16 @@ to quickly create a Cobra application.`, pkg.SetupProgress(10) trackers := pkg.GetTrackers() - trackers[pkg.TrackerStage0] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage0, 1)} - trackers[pkg.TrackerStage1] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage1, 1)} - trackers[pkg.TrackerStage2] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage2, 1)} - trackers[pkg.TrackerStage3] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage3, 1)} - trackers[pkg.TrackerStage4] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage4, 1)} - trackers[pkg.TrackerStage5] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage5, 3)} - trackers[pkg.TrackerStage6] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage6, 1)} - trackers[pkg.TrackerStage7] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage7, 3)} - trackers[pkg.TrackerStage8] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage8, 1)} - trackers[pkg.TrackerStage9] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TrackerStage9, 1)} + trackers[pkg.DownloadDependencies] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.DownloadDependencies, 3)} + trackers[pkg.GetAccountInfo] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.GetAccountInfo, 1)} + trackers[pkg.GetDNSInfo] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.GetDNSInfo, 1)} + trackers[pkg.TestHostedZoneLiveness] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.TestHostedZoneLiveness, 1)} + trackers[pkg.CloneAndDetokenizeGitOpsTemplate] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.CloneAndDetokenizeGitOpsTemplate, 1)} + trackers[pkg.CloneAndDetokenizeMetaphorTemplate] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.CloneAndDetokenizeMetaphorTemplate, 1)} + trackers[pkg.CreateSSHKey] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.CreateSSHKey, 1)} + trackers[pkg.CreateBuckets] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.CreateBuckets, 1)} + trackers[pkg.Detokenization] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.Detokenization, 1)} + trackers[pkg.SendTelemetry] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.SendTelemetry, 1)} infoCmd.Run(cmd, args) hostedZoneName, _ := cmd.Flags().GetString("hosted-zone-name") metricName := "kubefirst.init.started" @@ -85,47 +84,49 @@ to quickly create a Cobra application.`, viper.Set("aws.region", region) log.Println("region:", region) + // cluster name + clusterName, err := cmd.Flags().GetString("cluster-name") + if err != nil { + log.Panic(err) + } + viper.Set("cluster-name", clusterName) + log.Println("cluster-name:", clusterName) + + // version-gitops + versionGitOps, err := cmd.Flags().GetString("version-gitops") + if err != nil { + log.Panic(err) + } + viper.Set("version-gitops", versionGitOps) + log.Println("version-gitops:", versionGitOps) + viper.WriteConfig() - // refactor: confirm it (start) //! tracker 0 log.Println("installing kubefirst dependencies") - - downloadManager.DownloadTools(config, trackers) + trackers[pkg.DownloadDependencies].Tracker.Increment(1) + err = downloadManager.DownloadTools(config, trackers) + if err != nil { + log.Panic(err) + } log.Println("dependency installation complete") - trackers[pkg.TrackerStage0].Tracker.Increment(int64(1)) + trackers[pkg.DownloadDependencies].Tracker.Increment(1) //! tracker 1 log.Println("getting aws account information") aws.GetAccountInfo() log.Printf("aws account id: %s\naws user arn: %s", viper.GetString("aws.accountid"), viper.GetString("aws.userarn")) - trackers[pkg.TrackerStage1].Tracker.Increment(int64(1)) - // refactor: confirm it (end) + trackers[pkg.GetAccountInfo].Tracker.Increment(1) + //! tracker 2 // hosted zone id - // so we don't have to keep looking it up from the domain name to use it + // So we don't have to keep looking it up from the domain name to use it hostedZoneId := aws.GetDNSInfo(hostedZoneName) // viper values set in above function log.Println("hostedZoneId:", hostedZoneId) - trackers[pkg.TrackerStage0].Tracker.Increment(1) - trackers[pkg.TrackerStage1].Tracker.Increment(1) - - //cluster name - clusterName, err := cmd.Flags().GetString("cluster-name") - if err != nil { - log.Panic(err) - } - viper.Set("cluster-name", clusterName) - log.Println("cluster-name:", clusterName) - - //version-gitops - versionGitOps, err := cmd.Flags().GetString("version-gitops") - if err != nil { - log.Panic(err) - } - viper.Set("version-gitops", versionGitOps) - log.Println("version-gitops:", versionGitOps) + trackers[pkg.GetDNSInfo].Tracker.Increment(1) + //! tracker 3 // todo: this doesn't default to testing the dns check skipHostedZoneCheck := viper.GetBool("init.hostedzonecheck.enabled") if !skipHostedZoneCheck { @@ -133,66 +134,50 @@ to quickly create a Cobra application.`, } else { aws.TestHostedZoneLiveness(dryRun, hostedZoneName, hostedZoneId) } - trackers[pkg.TrackerStage2].Tracker.Increment(1) - - log.Println("creating an ssh key pair for your new cloud infrastructure") - pkg.CreateSshKeyPair() - log.Println("ssh key pair creation complete") - trackers[pkg.TrackerStage3].Tracker.Increment(1) - - log.Println("calling cloneGitOpsRepo()") - gitClient.CloneGitOpsRepo() - log.Println("cloneGitOpsRepo() complete") - trackers[pkg.TrackerStage4].Tracker.Increment(1) + trackers[pkg.TestHostedZoneLiveness].Tracker.Increment(1) - log.Println("calling download()") - trackers[pkg.TrackerStage5].Tracker.Increment(1) - err = downloadManager.DownloadTools(config, trackers) - if err != nil { - log.Panic(err) + //! tracker 4 + // todo: remove it after successful dry-run test + //log.Println("calling cloneGitOpsRepo()") + //gitClient.CloneGitOpsRepo() + //log.Println("cloneGitOpsRepo() complete") + // refactor: start + // TODO: get the below line added as a legit flag, don't merge with any value except kubefirst + gitopsTemplateGithubOrgOverride := "kubefirst" // <-- discussion point + log.Printf("cloning and detokenizing the gitops-template repository") + if gitopsTemplateGithubOrgOverride != "" { + log.Printf("using --gitops-template-gh-org=%s", gitopsTemplateGithubOrgOverride) } - trackers[pkg.TrackerStage5].Tracker.Increment(1) - - log.Println("download() complete") - log.Println("calling GetAccountInfo()") - aws.GetAccountInfo() - log.Println("GetAccountInfo() complete") - trackers[pkg.TrackerStage6].Tracker.Increment(1) + //! tracker 5 + prepareKubefirstTemplateRepo(config, gitopsTemplateGithubOrgOverride, "gitops") + log.Println("clone and detokenization of gitops-template repository complete") + trackers[pkg.CloneAndDetokenizeGitOpsTemplate].Tracker.Increment(int64(1)) + //! tracker 6 + log.Printf("cloning and detokenizing the metaphor-template repository") + prepareKubefirstTemplateRepo(config, "kubefirst", "metaphor") + log.Println("clone and detokenization of metaphor-template repository complete") + trackers[pkg.CloneAndDetokenizeMetaphorTemplate].Tracker.Increment(int64(1)) - log.Println("calling BucketRand()") - trackers[pkg.TrackerStage7].Tracker.Increment(1) + //! tracker 6 + log.Println("creating an ssh key pair for your new cloud infrastructure") + pkg.CreateSshKeyPair() + log.Println("ssh key pair creation complete") + trackers[pkg.CreateSSHKey].Tracker.Increment(1) - //! tracker 4 + //! tracker 7 //* should we consider going down to a single bucket //* for state and artifacts on open source? //* hitting a bucket limit on an install might deter someone log.Println("creating buckets for state and artifacts") aws.BucketRand(dryRun, trackers) - trackers[pkg.TrackerStage7].Tracker.Increment(1) + trackers[pkg.CreateBuckets].Tracker.Increment(1) log.Println("BucketRand() complete") log.Println("calling Detokenize()") pkg.Detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) log.Println("Detokenize() complete") - trackers[pkg.TrackerStage8].Tracker.Increment(1) - - // TODO: get the below line added as a legit flag, don't merge with any value except kubefirst - gitopsTemplateGithubOrgOverride := "kubefirst" // <-- discussion point - log.Printf("cloning and detokenizing the gitops-template repository") - if gitopsTemplateGithubOrgOverride != "" { - log.Printf("using --gitops-template-gh-org=%s", gitopsTemplateGithubOrgOverride) - } - - //! tracker 6 - prepareKubefirstTemplateRepo(config, gitopsTemplateGithubOrgOverride, "gitops") - log.Println("clone and detokenization of gitops-template repository complete") - trackers[pkg.TrackerStage6].Tracker.Increment(int64(1)) - //! tracker 7 - log.Printf("cloning and detokenizing the metaphor-template repository") - prepareKubefirstTemplateRepo(config, "kubefirst", "metaphor") - log.Println("clone and detokenization of metaphor-template repository complete") - trackers[pkg.TrackerStage7].Tracker.Increment(int64(1)) + trackers[pkg.Detokenization].Tracker.Increment(1) metricName = "kubefirst.init.completed" @@ -205,7 +190,7 @@ to quickly create a Cobra application.`, viper.WriteConfig() //! tracker 8 - trackers[pkg.TrackerStage9].Tracker.Increment(1) + trackers[pkg.SendTelemetry].Tracker.Increment(1) time.Sleep(time.Millisecond * 100) }, } diff --git a/internal/aws/aws.go b/internal/aws/aws.go index 36fac44c8..350581bb0 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -37,7 +37,7 @@ func BucketRand(dryRun bool, trackers map[string]*pkg.ActionTracker) { randomName := strings.ReplaceAll(autoname.Generate(), "_", "-") viper.Set("bucket.rand", randomName) - trackers[pkg.TrackerStage7].Tracker.Increment(int64(1)) + trackers[pkg.CloneAndDetokenizeMetaphorTemplate].Tracker.Increment(int64(1)) buckets := strings.Fields("state-store argo-artifacts gitlab-backup chartmuseum") for _, bucket := range buckets { diff --git a/internal/downloadManager/download.go b/internal/downloadManager/download.go index e4b43ef35..271295175 100644 --- a/internal/downloadManager/download.go +++ b/internal/downloadManager/download.go @@ -88,7 +88,7 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke } os.RemoveAll(fmt.Sprintf("%s/terraform.zip", toolsDir)) - trackers[pkg.TrackerStage5].Tracker.Increment(int64(1)) + trackers[pkg.DownloadDependencies].Tracker.Increment(int64(1)) helmVersion := config.HelmVersion helmDownloadUrl := fmt.Sprintf( diff --git a/pkg/progress_bar.go b/pkg/progress_bar.go index aec7f22ca..25d1af661 100644 --- a/pkg/progress_bar.go +++ b/pkg/progress_bar.go @@ -11,20 +11,26 @@ type ActionTracker struct { Tracker *progress.Tracker } -const TrackerStage0 = "1 - Load properties" -const TrackerStage1 = "2 - Set .flare initial values" -const TrackerStage2 = "3 - Test Domain Liveness" -const TrackerStage3 = "4 - Create SSH Key Pair" -const TrackerStage4 = "5 - Load Templates" -const TrackerStage5 = "6 - DownloadTools Tools" -const TrackerStage6 = "7 - Get Account Info" -const TrackerStage7 = "8 - Create Buckets" -const TrackerStage8 = "9 - Detokenize" -const TrackerStage9 = "10 - Send Telemetry" +const DownloadDependencies = "Download dependencies" +const GetAccountInfo = "Get Account Info" +const GetDNSInfo = "Get DNS Info" +const TestHostedZoneLiveness = "Test Domain Liveness" +const CloneAndDetokenizeGitOpsTemplate = "Clone and Detokenize (GitOps)" +const CloneAndDetokenizeMetaphorTemplate = "Clone and Detokenize (Metaphor)" +const CreateSSHKey = "Create SSH keys" +const CreateBuckets = "Create Buckets" +const Detokenization = "Detokenization" +const SendTelemetry = "Send Telemetry" -//const trackerStage5 = "6 - DownloadTools Tools" +const TrackerStage20 = "Apply Base" -const TrackerStage20 = "0 - Apply Base" +//const GetAccountInfo = "1 - Set .flare initial values" +//const CreateSSHKey = "1 - Load properties" +//const TestHostedZoneLiveness = "4 - Create SSH Key Pair" +//const TrackerStage4 = "5 - Load Templates" +//const CloneAndDetokenizeMetaphorTemplate = "8 - Create Buckets" +//const Detokenize1 = "9 - Detokenize" +//const trackerStage5 = "6 - DownloadTools Tools" //const trackerStage21 = "1 - Temporary SCM Install" //const trackerStage22 = "2 - Argo/Final SCM Install" @@ -77,8 +83,8 @@ func SetupProgress(numTrackers int) { // instantiate a Progress Writer and set up the options pw = progress.NewWriter() pw.SetAutoStop(*flagAutoStop) - pw.SetTrackerLength(30) - pw.SetMessageWidth(29) + pw.SetTrackerLength(40) + pw.SetMessageWidth(39) pw.SetNumTrackersExpected(*flagNumTrackers) pw.SetSortBy(progress.SortByPercentDsc) pw.SetStyle(progress.StyleDefault) From 95fc203ba0751de944b0794dde914cad3330398a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Mon, 11 Jul 2022 17:00:29 -0300 Subject: [PATCH 059/107] chore: update step names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- pkg/progress_bar.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/pkg/progress_bar.go b/pkg/progress_bar.go index 25d1af661..4f977eba8 100644 --- a/pkg/progress_bar.go +++ b/pkg/progress_bar.go @@ -22,20 +22,6 @@ const CreateBuckets = "Create Buckets" const Detokenization = "Detokenization" const SendTelemetry = "Send Telemetry" -const TrackerStage20 = "Apply Base" - -//const GetAccountInfo = "1 - Set .flare initial values" -//const CreateSSHKey = "1 - Load properties" -//const TestHostedZoneLiveness = "4 - Create SSH Key Pair" -//const TrackerStage4 = "5 - Load Templates" -//const CloneAndDetokenizeMetaphorTemplate = "8 - Create Buckets" -//const Detokenize1 = "9 - Detokenize" -//const trackerStage5 = "6 - DownloadTools Tools" - -//const trackerStage21 = "1 - Temporary SCM Install" -//const trackerStage22 = "2 - Argo/Final SCM Install" -//const trackerStage23 = "3 - Final Setup" - var ( pw progress.Writer Trackers map[string]*ActionTracker From b41468349af4f97078e31c63c88ab55d1599ab5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Mon, 11 Jul 2022 17:56:43 -0300 Subject: [PATCH 060/107] chore: update installation steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 20 ++++++++------------ cmd/init.go | 9 ++++----- pkg/progress_bar.go | 1 - 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 5fc55b8c8..80f6c1cec 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -276,27 +276,23 @@ to quickly create a Cobra application.`, // refactor: sounds like a new functions, should PushGitOpsToGitLab be renamed/update signature? gitlab.PushGitRepo(config, "gitlab", "gitops") // todo: need to handle if this was already pushed, errors on failure) - gitlab.PushGitOpsToGitLab(dryRun) + // todo: keep one of the two git push functions, they're similar, but not exactly the same + //gitlab.PushGitOpsToGitLab(dryRun) + + log.Println("pushing metaphor repo to origin gitlab") + gitlab.PushGitRepo(config, "gitlab", "metaphor") + // todo: keep one of the two git push functions, they're similar, but not exactly the same + //gitlab.PushGitOpsToGitLab(dryRun) + Trackers[trackerStage23].Tracker.Increment(int64(1)) Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlab.ChangeRegistryToGitLab(dryRun) Trackers[trackerStage22].Tracker.Increment(int64(1)) - // refactor: should this be removed? - gitlab.HydrateGitlabMetaphorRepo(dryRun) - Trackers[trackerStage23].Tracker.Increment(int64(1)) // todo triage / force apply the contents adjusting // todo kind: Application .repoURL: - - // refactor: should this be deleted? - //token := argocd.GetArgocdAuthToken(dryRun) - //argocd.SyncArgocdApplication(dryRun, "argo-components", token) - //argocd.SyncArgocdApplication(dryRun, "gitlab-runner-components", token) - //argocd.SyncArgocdApplication(dryRun, "gitlab-runner", token) - //argocd.SyncArgocdApplication(dryRun, "atlantis-components", token) - //argocd.SyncArgocdApplication(dryRun, "chartmuseum-components", token) } } diff --git a/cmd/init.go b/cmd/init.go index edd4eb5ea..57b804577 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -46,7 +46,6 @@ to quickly create a Cobra application.`, trackers[pkg.CloneAndDetokenizeMetaphorTemplate] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.CloneAndDetokenizeMetaphorTemplate, 1)} trackers[pkg.CreateSSHKey] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.CreateSSHKey, 1)} trackers[pkg.CreateBuckets] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.CreateBuckets, 1)} - trackers[pkg.Detokenization] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.Detokenization, 1)} trackers[pkg.SendTelemetry] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(pkg.SendTelemetry, 1)} infoCmd.Run(cmd, args) hostedZoneName, _ := cmd.Flags().GetString("hosted-zone-name") @@ -159,13 +158,13 @@ to quickly create a Cobra application.`, log.Println("clone and detokenization of metaphor-template repository complete") trackers[pkg.CloneAndDetokenizeMetaphorTemplate].Tracker.Increment(int64(1)) - //! tracker 6 + //! tracker 7 log.Println("creating an ssh key pair for your new cloud infrastructure") pkg.CreateSshKeyPair() log.Println("ssh key pair creation complete") trackers[pkg.CreateSSHKey].Tracker.Increment(1) - //! tracker 7 + //! tracker 8 //* should we consider going down to a single bucket //* for state and artifacts on open source? //* hitting a bucket limit on an install might deter someone @@ -174,10 +173,10 @@ to quickly create a Cobra application.`, trackers[pkg.CreateBuckets].Tracker.Increment(1) log.Println("BucketRand() complete") + //! tracker 9 log.Println("calling Detokenize()") pkg.Detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) log.Println("Detokenize() complete") - trackers[pkg.Detokenization].Tracker.Increment(1) metricName = "kubefirst.init.completed" @@ -189,7 +188,7 @@ to quickly create a Cobra application.`, viper.WriteConfig() - //! tracker 8 + //! tracker 10 trackers[pkg.SendTelemetry].Tracker.Increment(1) time.Sleep(time.Millisecond * 100) }, diff --git a/pkg/progress_bar.go b/pkg/progress_bar.go index 4f977eba8..532ab3418 100644 --- a/pkg/progress_bar.go +++ b/pkg/progress_bar.go @@ -19,7 +19,6 @@ const CloneAndDetokenizeGitOpsTemplate = "Clone and Detokenize (GitOps)" const CloneAndDetokenizeMetaphorTemplate = "Clone and Detokenize (Metaphor)" const CreateSSHKey = "Create SSH keys" const CreateBuckets = "Create Buckets" -const Detokenization = "Detokenization" const SendTelemetry = "Send Telemetry" var ( From 97b0940dd973c6ad0b901c33a674b6eeb6c4e306 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:13:37 +0000 Subject: [PATCH 061/107] Rename repo Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/argocdSync.go | 2 +- cmd/checktools.go | 4 ++-- cmd/clean.go | 2 +- cmd/create.go | 18 +++++++++--------- cmd/destroy.go | 10 +++++----- cmd/info.go | 2 +- cmd/init.go | 10 +++++----- cmd/kubefirstTemplate.go | 2 +- cmd/kubernetes.go | 2 +- cmd/root.go | 2 +- cmd/version.go | 2 +- go.mod | 2 +- internal/aws/aws.go | 2 +- internal/downloadManager/download.go | 4 ++-- internal/gitClient/git.go | 4 ++-- internal/gitlab/gitlab.go | 6 +++--- internal/helm/helm.go | 2 +- internal/k8s/kubernetes.go | 2 +- internal/softserve/softserve.go | 6 +++--- internal/terraform/terraform.go | 4 ++-- internal/vault/vault.go | 4 ++-- main.go | 2 +- pkg/keys.go | 2 +- 23 files changed, 48 insertions(+), 48 deletions(-) diff --git a/cmd/argocdSync.go b/cmd/argocdSync.go index bc1a7a732..9910bc33c 100644 --- a/cmd/argocdSync.go +++ b/cmd/argocdSync.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/kubefirst/nebulous/internal/argocd" + "github.com/kubefirst/kubefirst/internal/argocd" "log" "github.com/spf13/cobra" diff --git a/cmd/checktools.go b/cmd/checktools.go index f962fdaf0..7592ae4ad 100644 --- a/cmd/checktools.go +++ b/cmd/checktools.go @@ -2,8 +2,8 @@ package cmd import ( "fmt" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" ) diff --git a/cmd/clean.go b/cmd/clean.go index 96265a411..052dd24cd 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/kubefirst/configs" "log" "os" diff --git a/cmd/create.go b/cmd/create.go index 80f6c1cec..ac8d60798 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -2,15 +2,15 @@ package cmd import ( "fmt" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/argocd" - "github.com/kubefirst/nebulous/internal/gitlab" - "github.com/kubefirst/nebulous/internal/helm" - "github.com/kubefirst/nebulous/internal/softserve" - "github.com/kubefirst/nebulous/internal/telemetry" - "github.com/kubefirst/nebulous/internal/terraform" - "github.com/kubefirst/nebulous/internal/vault" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/argocd" + "github.com/kubefirst/kubefirst/internal/gitlab" + "github.com/kubefirst/kubefirst/internal/helm" + "github.com/kubefirst/kubefirst/internal/softserve" + "github.com/kubefirst/kubefirst/internal/telemetry" + "github.com/kubefirst/kubefirst/internal/terraform" + "github.com/kubefirst/kubefirst/internal/vault" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" "k8s.io/client-go/kubernetes" diff --git a/cmd/destroy.go b/cmd/destroy.go index 31d8788d9..2e12de97c 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -1,11 +1,11 @@ package cmd import ( - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/aws" - "github.com/kubefirst/nebulous/internal/gitlab" - "github.com/kubefirst/nebulous/internal/k8s" - "github.com/kubefirst/nebulous/internal/terraform" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/aws" + "github.com/kubefirst/kubefirst/internal/gitlab" + "github.com/kubefirst/kubefirst/internal/k8s" + "github.com/kubefirst/kubefirst/internal/terraform" "github.com/spf13/cobra" "log" "os" diff --git a/cmd/info.go b/cmd/info.go index 84d7649d8..6863af66d 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/kubefirst/configs" "github.com/spf13/cobra" "log" "runtime" diff --git a/cmd/init.go b/cmd/init.go index 57b804577..337c6d4a4 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -6,11 +6,11 @@ import ( "strings" "time" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/aws" - "github.com/kubefirst/nebulous/internal/downloadManager" - "github.com/kubefirst/nebulous/internal/telemetry" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/aws" + "github.com/kubefirst/kubefirst/internal/downloadManager" + "github.com/kubefirst/kubefirst/internal/telemetry" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" ) diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 8a8ce857b..629ae4cd1 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -2,7 +2,7 @@ package cmd import ( "fmt" - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/kubefirst/configs" "io/ioutil" "log" "os" diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index 6eb1c00ed..9da7c77b1 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -9,7 +9,7 @@ import ( "context" "encoding/json" "fmt" - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/kubefirst/configs" "log" "os" "os/exec" diff --git a/cmd/root.go b/cmd/root.go index 5b9141ddd..b2f2e3eb8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,7 +2,7 @@ package cmd import ( "errors" - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/kubefirst/configs" "github.com/spf13/cobra" "github.com/spf13/viper" "log" diff --git a/cmd/version.go b/cmd/version.go index d57076b36..4a8a7c0c3 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/kubefirst/configs" "github.com/spf13/cobra" "log" ) diff --git a/go.mod b/go.mod index 3470172ab..78ec3e2d8 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/kubefirst/nebulous +module github.com/kubefirst/kubefirst go 1.17 diff --git a/internal/aws/aws.go b/internal/aws/aws.go index 350581bb0..0128ef03c 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -12,7 +12,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/cip8/autoname" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" "log" "net" diff --git a/internal/downloadManager/download.go b/internal/downloadManager/download.go index 271295175..7414ff69e 100644 --- a/internal/downloadManager/download.go +++ b/internal/downloadManager/download.go @@ -5,8 +5,8 @@ import ( "archive/zip" "compress/gzip" "fmt" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/pkg" "io" "log" "net/http" diff --git a/internal/gitClient/git.go b/internal/gitClient/git.go index cc6516da6..db1433792 100644 --- a/internal/gitClient/git.go +++ b/internal/gitClient/git.go @@ -9,8 +9,8 @@ import ( "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" "golang.org/x/crypto/ssh" ) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 096d8e317..be29f1cd6 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -16,9 +16,9 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" gitHttp "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/google/uuid" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/k8s" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/k8s" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" "html/template" v1 "k8s.io/api/core/v1" diff --git a/internal/helm/helm.go b/internal/helm/helm.go index c22e5f8d4..aff58b71e 100644 --- a/internal/helm/helm.go +++ b/internal/helm/helm.go @@ -2,7 +2,7 @@ package helm import ( "fmt" - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/kubefirst/configs" "github.com/spf13/viper" "log" "os" diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index e133a6214..9f4ffeaa0 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -8,7 +8,7 @@ import ( "context" "encoding/json" "fmt" - "github.com/kubefirst/nebulous/internal/argocd" + "github.com/kubefirst/kubefirst/internal/argocd" "github.com/spf13/viper" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" diff --git a/internal/softserve/softserve.go b/internal/softserve/softserve.go index 6dbec4724..0662c6052 100644 --- a/internal/softserve/softserve.go +++ b/internal/softserve/softserve.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/gitlab" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/gitlab" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" ssh2 "golang.org/x/crypto/ssh" "io/ioutil" diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index b7134164b..e8e80e641 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -2,8 +2,8 @@ package terraform import ( "fmt" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/pkg" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" "log" "os" diff --git a/internal/vault/vault.go b/internal/vault/vault.go index ed31ec478..68217e46e 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -5,8 +5,8 @@ import ( "encoding/json" "fmt" vault "github.com/hashicorp/vault/api" - "github.com/kubefirst/nebulous/configs" - "github.com/kubefirst/nebulous/internal/k8s" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/k8s" "github.com/spf13/viper" gitlab "github.com/xanzy/go-gitlab" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/main.go b/main.go index 786bf86ab..182adac79 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,7 @@ package main import ( "fmt" - "github.com/kubefirst/nebulous/cmd" + "github.com/kubefirst/kubefirst/cmd" "log" "os" "time" diff --git a/pkg/keys.go b/pkg/keys.go index 7fbb024a2..b9d388acc 100644 --- a/pkg/keys.go +++ b/pkg/keys.go @@ -7,7 +7,7 @@ import ( "encoding/pem" "fmt" goGitSsh "github.com/go-git/go-git/v5/plumbing/transport/ssh" - "github.com/kubefirst/nebulous/configs" + "github.com/kubefirst/kubefirst/configs" "github.com/spf13/viper" "golang.org/x/crypto/ssh" "io/ioutil" From f98a32491738488e87f2cce7be99ef51c1a10972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Tue, 12 Jul 2022 11:53:28 -0300 Subject: [PATCH 062/107] docs: update readme with more general details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- README.md | 219 +++++----------------------------------------- cmd/argocdSync.go | 2 +- cmd/info.go | 2 +- 3 files changed, 24 insertions(+), 199 deletions(-) diff --git a/README.md b/README.md index fe05c74ed..cf5dba889 100644 --- a/README.md +++ b/README.md @@ -1,134 +1,3 @@ -# nebulous -The Kubefirst Open Source Platform - -![images/nebulous-arch.png](images/nebulous-arch.png) - -## tl;dr: -- step 1: establish a new aws account with a single hosted zone that's configured to receive traffic from your domain name registrar -- step 2: add your 6 configuration values to kubefirst.env and run the nebulous container -- step 3: get a fully-functioning application delivery ecosystem, complete with kubernetes, gitops, vault, terraform, atlantis, gitlab, gitlab-runner, and a sample app that demonstrates how it all works. - ---- - -# user guide - -## docs -- [introduction](https://docs.kubefirst.com/) -- [installation](https://docs.kubefirst.com/nebulous/install.html) -- [getting familiar](https://docs.kubefirst.com/kubefirst/getting-started.html) -- [teardown](https://docs.kubefirst.com/nebulous/teardown.html) -- [faq](https://docs.kubefirst.com/nebulous/faq.html) -- [contact](https://docs.kubefirst.com/contact.html) - ---- - -# contributor guide - -The docs above are tailored to our end user's experience. However things are a little different if you're contributing to nebulous itself. The docs that follow are intended only for source contributors. - -### step 1 - setup nebulous.env - -For a first run, this step is no different than the guidance to our end users, you need to set up a `kubefirst.env` in the nebulous repo's root directory. You can create the file template by running this from your terminal, editing with your values with the normal settings. - -For subsequent executions, especially while debugging, it's sometimes helpful to use some additional environment variables that allow you to control the flow of execution. See the notes in each section for details on controlling your debugging. - -In addition to the flow controls, you'll also find some hack comments by the various terraform apply commands. This allows you to change apply commands to exiting deploy commands. This can also be valuable when you need a mulligan on a particular section. - -```bash -cat << EOF > kubefirst.env -############################### -# Access settings -# The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are your credentials to -# log into your AWS account, you can often find these in `~/.aws/credentials` -# The AWS_DEFAULT_REGION is the aws region that your new infrastructure will provision in - -# The AWS_HOSTED_ZONE_NAME is the domain name associated with your prerequesite hosted zone in route53 - it should look similar to yourdomain.com with no www. prefix and no . suffix - -AWS_ACCESS_KEY_ID=YOUR_ADMIN_AWS_ACCESS_KEY_ID -AWS_SECRET_ACCESS_KEY=YOUR_ADMIN_AWS_SECRET_ACCESS_KEY -AWS_HOSTED_ZONE_NAME=yourdomain.com -AWS_DEFAULT_REGION=us-east-2 - - -################### -# Admin settings -# The EMAIL_ADDRESS is used for the ssh key that's generated and for certificate expiration notifications -# The GITLAB_BOT_ROOT_PASSWORD is the password to use for the gitlab root user, change this to a value only you know - -EMAIL_ADDRESS=YOUR_EMAIL_ADDRESS@yourdomain.com -GITLAB_BOT_ROOT_PASSWORD=123456ABCDEF! - - -############################### -# Users: -# The BUCKET_RAND needs to be set and uncommented before destroy, see the teardown -# docs for details. -# -# Contributors: -# The BUCKET_RAND has implications on bucket reuse when iterating -# once you successfully get past base terraform apply, -# take the random suffix that was generated, apply it to the -# next line, and start reusing the bucket for subsequent runs. -# if you don't set this value on subsequent runs, it will keep -# generating new buckets for you. You can find this value in the -# nebulous execution output. -# -# BUCKET_RAND=abc123 - - -############################### -# Note: Operational Flow Controls - uncomment the items below -# when you want to skip over various sections. Leaving them -# all commented like they are here will execute everything. -# -# -# SKIP_HZ_CHECK=true -# SKIP_DETOKENIZATION=true -# SKIP_BASE_APPLY=true -# SKIP_GITLAB_RECONFIG=true -# SKIP_GITLAB_APPLY=true -# SKIP_ARGOCD_APPLY=true -# SKIP_VAULT_APPLY=true -# SKIP_SSH_STORAGE=true -# SKIP_USERS_APPLY=true -# SKIP_OIDC_PATCHING=true - -EOF -``` - -### step 2 - build nebulous locally - -Come up with local tag name for your nebulous image. We'll use `foo` as our example local tag name in these docs. To build the `foo` tag of nebulous run the following from your local nebulous repo root directory. - -```bash -docker build . -t nebulous:foo -``` - -### step 3 - running nebulous - -Once you have built the `nebulous:foo` image as shown above, you can kickoff the automated init script by running the following. The difference between this guidance and the end user guidance is that this mounts the `gitops`, `scripts`, and `git` directories to your localhost volume so you can negotiate changes to the runtime environment on the fly. - -This is how you run the container with the volume mounts. Run this from your nebulous directory: -``` -docker run -it --env-file=kubefirst.env -v $PWD/gitops:/gitops -v $PWD/metaphor:/metaphor -v $PWD/scripts:/scripts -v $PWD/git:/git --entrypoint /scripts/nebulous/init.sh nebulous:foo -``` - -### step 4 - teardown (once you're ready to tear it all back down, obviously) - -There are a few things to note about teardown. - -Nebulous creates a VPC, some subnets, a gitlab server, a kubernetes cluster, some policies, roles, and a few other things (complete list in the teardown docs). Terraform knows about all of these things, and if you only created these resources, you'll be able to run teardown without thinking too hard. - -However, terraform is only able to destroy resources that are managed in terraform. It doesn't know about things you do manually. Anything you may have added through non-terraform operations must be manually removed before running the teardown script. - -Let's consider, for example, a scenario where you manually `helm install`ed an app to your new cluster, and that app spins up a new load balancer in your VPC. If you don't remove that app and its load balancer before running destroy, you won't be able to complete the terraform destroy operation. This is because you can't remove a VPC that still has a live load balancer running in it. - -With that context in mind, once you've removed the manual things you may have added to this environment, you can kickoff the automated destroy script by running: -``` -docker run -it --env-file=kubefirst.env -v $PWD/gitops:/gitops -v $PWD/metaphor:/metaphor -v $PWD/scripts:/scripts -v $PWD/git:/git --entrypoint /scripts/nebulous/destroy.sh nebulous:foo -``` - -### New README - # Kubefirst CLI Kubefirst CLI is a cloud provisioning tool. With simple setup and few CLI calls, we spin up a full AWS cluster with full @@ -140,6 +9,7 @@ GitOps integration, secrets management, production and development Kubernetes en - [Creation](#creation) - [Access ArgoCD](#access-argocd) - [Destroy](#destroy) +- [Available Commands]() ## Setup @@ -148,8 +18,8 @@ The setup is extremely simple, create a `.env` file in the root folder, and add | Variable | example | |--------------------|------------------| | AWS_PROFILE | default | -| CLOUD_PROVIDER=aws | aws | | AWS_REGION | us-east-1 | +| CLOUD_PROVIDER=aws | aws | | HOSTED_ZONE_NAME | example.com | | ADMIN_EMAIL | john@example.com | @@ -166,9 +36,8 @@ docker-compose up kubefirst-dev Some process requires previous initialization, for that, run: ```bash -touch ~/.flare mkdir -p ~/.kubefirst -go run . nebulous init --admin-email $ADMIN_EMAIL --cloud $CLOUD_PROVIDER --hosted-zone-name $HOSTED_ZONE_NAME --region $AWS_REGION +go run . init --admin-email $ADMIN_EMAIL --cloud $CLOUD_PROVIDER --hosted-zone-name $HOSTED_ZONE_NAME --region $AWS_REGION ``` ## Creation @@ -176,7 +45,7 @@ go run . nebulous init --admin-email $ADMIN_EMAIL --cloud $CLOUD_PROVIDER --host At this point, everything is ready to start provisioning the cloud services, and for that we can run: ```bash -./bin/flare nebulous create +go run . create ``` ## Access ArgoCD @@ -189,75 +58,31 @@ kubectl -n argocd port-forward svc/argocd-server 8080:80 ## Destroy -To destroy remote then local. - -These environment variables are expected: (todo: move all env. variable references to the setup section) - -| Variable | example | -|------------------|-----------------------------------------------------------------------------------------------| -| AWS_PROFILE | default | -| AWS_REGION | us-east-1 | -| AWS_ACCOUNT_ID | 000000000 | -| HOSTED_ZONE_NAME | kubefast.com | -| GITLAB_TOKEN | "xxxxx1-xx1x-x1xx-1" # replace with value from ~/.flare (only needed if you got to gitlab tf) | +It will destroy the kubefirst management cluster, and clean up every change made in the cloud. ```bash -./bin/flare nebulous destroy + +go run . destroy rm -rf ~/.kubefirst rm ~/.flare ``` -#### Notes: - -added gitlab.yaml to registry -pushing local to soft origin +## Available Commands ---- -todo: this is coming from cmd/README.md , we need to format it: +Kubefirst provides extra tooling for handling the provisioning work. -# Overview +| Command | Description | +|------------|-----------------------------------------------------------| +| argocdSync | Request ArgoCD to synchronize applications | +| checktools | use to check compatibility of .kubefirst/tools | +| clean | removes all kubefirst resources locally for new execution | +| create | create a kubefirst management cluster | +| destroy | destroy the kubefirst management cluster | +| info | provides general Kubefirst setup data | +| init | initialize your local machine to execute `create` | +| version | print the version number for kubefirst-cli" | -General overview of the code, to help shuffling parts around. - -# Globals Variables - -| Variable|Type | Use | -|:--|:--|:--| -|cleanCmd|Command| Clean command| -|createCmd|Command| Create command| -|destroyCmd|Command| Destroy command| -|nebulousCmd|Command| CLI main command | -|versionCmd|Command| Version command| -|rootCmd|Command| Root command command| -|infoCmd|Command| Info command| -|initCmd|Command| Init command - pre-provision command| -|home|String|User Home dir or Work dir| -|kubectlClientPath|String|Kubectl CLI path| -|kubeconfigPath|String|Kubeconfig Path| -|localOs|String|Host OS| -|localArchitecture|String|Host OS architecture| -|terraformPath|String|Terraform CLI path| -|helmClientPath|String|Helm CLI path| -|dryrunMode|Bool|If installer should run in dry-run mode| -|Trackers|Trecker|Map of trackers| -|vaultRootToken|String|Root token for vault| -|gitlabToolboxPodName|String|Toolbox pod name| -|gitlabSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| -|vaultSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| -|argocdSecretClient|coreV1Types.SecretInterface|Client shorthand to interface| -|gitlabSecretClient|coreV1Types.PodInterface|Client shorthand to interface| -|cfgFile|String| .flare config file| -|NebolousVersion|String|CLI version| - - -# Commands Available +#### Notes: -| Command | Short Description | Long Description| -|:---|:---|:---| -|checktools|Present, needs review.|Present, needs review.| -|clean|Missing Text|Missing Text| -|create|Missing Text|Missing Text| -|destroy|Missing Text|Missing Text| -|info|Present, needs review.|Present, needs review.| -|init|Missing Text|Missing Text| -|version|Present, needs review.|Present, needs review.| +added gitlab.yaml to registry +pushing local to soft origin diff --git a/cmd/argocdSync.go b/cmd/argocdSync.go index bc1a7a732..ae7415f75 100644 --- a/cmd/argocdSync.go +++ b/cmd/argocdSync.go @@ -11,7 +11,7 @@ import ( // argocdSyncCmd represents the argocdSync command var argocdSyncCmd = &cobra.Command{ Use: "argocdSync", - Short: "A brief description of your command", + Short: "request ArgoCD to synchronize applications", Long: `A longer description that spans multiple lines and likely contains examples and usage of using your command. For example: diff --git a/cmd/info.go b/cmd/info.go index 84d7649d8..76792791e 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -11,7 +11,7 @@ import ( // infoCmd represents the info command var infoCmd = &cobra.Command{ Use: "info", - Short: "Provides general Kubefirst setup data", + Short: "provides general Kubefirst setup data", Long: `Provides machine data, files and folders paths`, Run: func(cmd *cobra.Command, args []string) { From 76d3db87939301281d7cacf6ac8a61861ce3e0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Tue, 12 Jul 2022 11:56:41 -0300 Subject: [PATCH 063/107] docs: fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/version.go b/cmd/version.go index d57076b36..64a4f1359 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -12,7 +12,7 @@ func init() { var versionCmd = &cobra.Command{ Use: "version", - Short: "Print the version number for kubefirst-cli", + Short: "print the version number for kubefirst-cli", Long: `All software has versions. This is kubefirst's`, Run: func(cmd *cobra.Command, args []string) { config := configs.ReadConfig() From e7f089025b4613f73bf7c85433805e9cf00a8da6 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:48:41 +0000 Subject: [PATCH 064/107] reapply branch code Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/init.go | 6 +++--- cmd/kubefirstTemplate.go | 13 ++++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 337c6d4a4..e95f06790 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -9,7 +9,7 @@ import ( "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/internal/aws" "github.com/kubefirst/kubefirst/internal/downloadManager" - "github.com/kubefirst/kubefirst/internal/telemetry" + "github.com/kubefirst/kubefirst/internal/telemetry" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -149,12 +149,12 @@ to quickly create a Cobra application.`, } //! tracker 5 - prepareKubefirstTemplateRepo(config, gitopsTemplateGithubOrgOverride, "gitops") + prepareKubefirstTemplateRepo(config, gitopsTemplateGithubOrgOverride, "gitops",viper.GetString("version-gitops")) log.Println("clone and detokenization of gitops-template repository complete") trackers[pkg.CloneAndDetokenizeGitOpsTemplate].Tracker.Increment(int64(1)) //! tracker 6 log.Printf("cloning and detokenizing the metaphor-template repository") - prepareKubefirstTemplateRepo(config, "kubefirst", "metaphor") + prepareKubefirstTemplateRepo(config, "kubefirst", "metaphor","main") log.Println("clone and detokenization of metaphor-template repository complete") trackers[pkg.CloneAndDetokenizeMetaphorTemplate].Tracker.Increment(int64(1)) diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 629ae4cd1..92fe12f66 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -9,21 +9,28 @@ import ( "path/filepath" "strings" "time" - + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5" gitConfig "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing/object" "github.com/spf13/viper" ) -func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName string) { +func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName string, branch string) { + + if branch == "" { + branch = "main" + } repoUrl := fmt.Sprintf("https://github.com/%s/%s-template", githubOrg, repoName) directory := fmt.Sprintf("%s/.kubefirst/%s", config.HomePath, repoName) log.Println("git clone", repoUrl, directory) + log.Println("git clone -b ", branch, repoUrl, directory) repo, err := git.PlainClone(directory, false, &git.CloneOptions{ URL: repoUrl, + ReferenceName: plumbing.NewBranchReferenceName(branch), + SingleBranch: true, }) if err != nil { log.Panicf("error cloning %s-template repository from github %s", repoName, err) From 2d7157c22a0e15201634d88f6ea0a018af94a64a Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 12 Jul 2022 18:08:15 +0000 Subject: [PATCH 065/107] revert terraform action Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/terraform/terraform.go | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index e8e80e641..336b9ec5b 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -8,6 +8,8 @@ import ( "log" "os" "strings" + "bytes" + "os/exec" ) func ApplyBaseTerraform(dryRun bool, directory string) { @@ -27,20 +29,31 @@ func ApplyBaseTerraform(dryRun bool, directory string) { if err != nil { log.Panicf("error, directory does not exist - did you `kubefirst init`?: %s \nerror: %v", directory, err) } - _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") + terraformInit := exec.Command(config.TerraformPath, "init") + terraformInit.Stdout = os.Stdout + terraformInit.Stderr = os.Stderr + errInit := terraformInit.Run() if errInit != nil { log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) } - _, _, errApply := pkg.ExecShellReturnStrings(config.TerraformPath, "apply", "-auto-approve") + terraformApply := exec.Command(config.TerraformPath, "apply", "-auto-approve") + terraformApply.Stdout = os.Stdout + terraformApply.Stderr = os.Stderr + errApply := terraformApply.Run() if errApply != nil { log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) } - keyOut, _, errKey := pkg.ExecShellReturnStrings(config.TerraformPath, "output", "vault_unseal_kms_key") + + var keyOut bytes.Buffer + k := exec.Command(config.TerraformPath, "output", "vault_unseal_kms_key") + k.Stdout = &keyOut + k.Stderr = os.Stderr + errKey := k.Run() if errKey != nil { log.Panicf("error: terraform apply failed %v", err) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) - keyIdNoSpace := strings.TrimSpace(keyOut) + keyIdNoSpace := strings.TrimSpace(keyOut.String()) keyId := keyIdNoSpace[1 : len(keyIdNoSpace)-1] log.Println("keyid is:", keyId) viper.Set("vault.kmskeyid", keyId) @@ -65,12 +78,18 @@ func DestroyBaseTerraform(skipBaseTerraform bool) { os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - _, _, errInit := pkg.ExecShellReturnStrings(config.TerraformPath, "init") + terraformInit := exec.Command(config.TerraformPath, "init") + terraformInit.Stdout = os.Stdout + terraformInit.Stderr = os.Stderr + errInit := terraformInit.Run() if errInit != nil { log.Panicf("failed to terraform init base %v", err) } - _, _, errDestroy := pkg.ExecShellReturnStrings(config.TerraformPath, "destroy", "-auto-approve") + terraformDestroy := exec.Command(config.TerraformPath, "destroy", "-auto-approve") + terraformDestroy.Stdout = os.Stdout + terraformDestroy.Stderr = os.Stderr + errDestroy := terraformDestroy.Run() if errDestroy != nil { log.Panicf("failed to terraform destroy base %v", err) } From 4d74f476a72d0c5dd7fc512cf2998944ab56d50f Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 12 Jul 2022 18:33:43 +0000 Subject: [PATCH 066/107] fix err Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/terraform/terraform.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index 336b9ec5b..90da44e7d 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -34,14 +34,14 @@ func ApplyBaseTerraform(dryRun bool, directory string) { terraformInit.Stderr = os.Stderr errInit := terraformInit.Run() if errInit != nil { - log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) + log.Panic(fmt.Sprintf("error: terraform init failed %v", errInit)) } terraformApply := exec.Command(config.TerraformPath, "apply", "-auto-approve") terraformApply.Stdout = os.Stdout terraformApply.Stderr = os.Stderr errApply := terraformApply.Run() if errApply != nil { - log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) + log.Panic(fmt.Sprintf("error: terraform init failed %v", errApply)) } var keyOut bytes.Buffer @@ -50,7 +50,7 @@ func ApplyBaseTerraform(dryRun bool, directory string) { k.Stderr = os.Stderr errKey := k.Run() if errKey != nil { - log.Panicf("error: terraform apply failed %v", err) + log.Panicf("error: terraform apply failed %v", errKey) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) keyIdNoSpace := strings.TrimSpace(keyOut.String()) @@ -83,7 +83,7 @@ func DestroyBaseTerraform(skipBaseTerraform bool) { terraformInit.Stderr = os.Stderr errInit := terraformInit.Run() if errInit != nil { - log.Panicf("failed to terraform init base %v", err) + log.Panicf("failed to terraform init base %v", errInit) } terraformDestroy := exec.Command(config.TerraformPath, "destroy", "-auto-approve") @@ -91,7 +91,7 @@ func DestroyBaseTerraform(skipBaseTerraform bool) { terraformDestroy.Stderr = os.Stderr errDestroy := terraformDestroy.Run() if errDestroy != nil { - log.Panicf("failed to terraform destroy base %v", err) + log.Panicf("failed to terraform destroy base %v", errDestroy) } viper.Set("destroy.terraformdestroy.base", true) viper.WriteConfig() From c2092aee47091bbf82e7ebd8a540cf28db67f8f6 Mon Sep 17 00:00:00 2001 From: Jared Edwards Date: Tue, 12 Jul 2022 12:48:38 -0600 Subject: [PATCH 067/107] re-ordering the functions (#89) --- cmd/init.go | 49 ++++++++++++++++++------------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 337c6d4a4..f5ca1cce3 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -1,7 +1,6 @@ package cmd import ( - "fmt" "log" "strings" "time" @@ -103,7 +102,6 @@ to quickly create a Cobra application.`, //! tracker 0 log.Println("installing kubefirst dependencies") - trackers[pkg.DownloadDependencies].Tracker.Increment(1) err = downloadManager.DownloadTools(config, trackers) if err != nil { log.Panic(err) @@ -136,48 +134,37 @@ to quickly create a Cobra application.`, trackers[pkg.TestHostedZoneLiveness].Tracker.Increment(1) //! tracker 4 - // todo: remove it after successful dry-run test - //log.Println("calling cloneGitOpsRepo()") - //gitClient.CloneGitOpsRepo() - //log.Println("cloneGitOpsRepo() complete") - // refactor: start + //* should we consider going down to a single bucket + //* for state and artifacts on open source? + //* hitting a bucket limit on an install might deter someone + log.Println("creating buckets for state and artifacts") + aws.BucketRand(dryRun, trackers) + trackers[pkg.CreateBuckets].Tracker.Increment(1) + log.Println("BucketRand() complete") + + //! tracker 5 + log.Println("creating an ssh key pair for your new cloud infrastructure") + pkg.CreateSshKeyPair() + log.Println("ssh key pair creation complete") + trackers[pkg.CreateSSHKey].Tracker.Increment(1) + + //! tracker 6 // TODO: get the below line added as a legit flag, don't merge with any value except kubefirst gitopsTemplateGithubOrgOverride := "kubefirst" // <-- discussion point log.Printf("cloning and detokenizing the gitops-template repository") if gitopsTemplateGithubOrgOverride != "" { log.Printf("using --gitops-template-gh-org=%s", gitopsTemplateGithubOrgOverride) } - - //! tracker 5 prepareKubefirstTemplateRepo(config, gitopsTemplateGithubOrgOverride, "gitops") log.Println("clone and detokenization of gitops-template repository complete") trackers[pkg.CloneAndDetokenizeGitOpsTemplate].Tracker.Increment(int64(1)) - //! tracker 6 + + //! tracker 7 log.Printf("cloning and detokenizing the metaphor-template repository") prepareKubefirstTemplateRepo(config, "kubefirst", "metaphor") log.Println("clone and detokenization of metaphor-template repository complete") trackers[pkg.CloneAndDetokenizeMetaphorTemplate].Tracker.Increment(int64(1)) - //! tracker 7 - log.Println("creating an ssh key pair for your new cloud infrastructure") - pkg.CreateSshKeyPair() - log.Println("ssh key pair creation complete") - trackers[pkg.CreateSSHKey].Tracker.Increment(1) - - //! tracker 8 - //* should we consider going down to a single bucket - //* for state and artifacts on open source? - //* hitting a bucket limit on an install might deter someone - log.Println("creating buckets for state and artifacts") - aws.BucketRand(dryRun, trackers) - trackers[pkg.CreateBuckets].Tracker.Increment(1) - log.Println("BucketRand() complete") - - //! tracker 9 - log.Println("calling Detokenize()") - pkg.Detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) - log.Println("Detokenize() complete") - metricName = "kubefirst.init.completed" if !dryRun { @@ -188,7 +175,7 @@ to quickly create a Cobra application.`, viper.WriteConfig() - //! tracker 10 + //! tracker 8 trackers[pkg.SendTelemetry].Tracker.Increment(1) time.Sleep(time.Millisecond * 100) }, From e4154e12a20faa5c7364b841efca093b519f4d6c Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 12 Jul 2022 19:07:00 +0000 Subject: [PATCH 068/107] fix change Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/init.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 010331b02..8b55585d5 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -155,13 +155,13 @@ to quickly create a Cobra application.`, if gitopsTemplateGithubOrgOverride != "" { log.Printf("using --gitops-template-gh-org=%s", gitopsTemplateGithubOrgOverride) } - prepareKubefirstTemplateRepo(config, gitopsTemplateGithubOrgOverride, "gitops") + prepareKubefirstTemplateRepo(config, gitopsTemplateGithubOrgOverride, "gitops", viper.GetString("version-gitops")) log.Println("clone and detokenization of gitops-template repository complete") trackers[pkg.CloneAndDetokenizeGitOpsTemplate].Tracker.Increment(int64(1)) //! tracker 7 log.Printf("cloning and detokenizing the metaphor-template repository") - prepareKubefirstTemplateRepo(config, "kubefirst", "metaphor") + prepareKubefirstTemplateRepo(config, "kubefirst", "metaphor", "main") log.Println("clone and detokenization of metaphor-template repository complete") trackers[pkg.CloneAndDetokenizeMetaphorTemplate].Tracker.Increment(int64(1)) From 16c891d07b5ae7ed562cc5d7323808ef8ca9dc71 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Tue, 12 Jul 2022 19:12:46 +0000 Subject: [PATCH 069/107] fix change again Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/init.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/init.go b/cmd/init.go index 8b55585d5..64f72d329 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -102,6 +102,7 @@ to quickly create a Cobra application.`, //! tracker 0 log.Println("installing kubefirst dependencies") + trackers[pkg.DownloadDependencies].Tracker.Increment(1) err = downloadManager.DownloadTools(config, trackers) if err != nil { log.Panic(err) From 78fbe919f193fbafe16373da61658814feea3e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Tue, 12 Jul 2022 16:58:14 -0300 Subject: [PATCH 070/107] feat: expose argocd container port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- docker-compose.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yaml b/docker-compose.yaml index 2f193a56c..3da69c110 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,6 +7,8 @@ services: build: context: ./build container_name: kubefirst-dev + ports: + - "8080:8080" # ArgoCD volumes: - ./:/home/developer/kubefirst env_file: From 1edef42034d7adbfa42bafc96dc1b945076c9367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Tue, 12 Jul 2022 17:49:59 -0300 Subject: [PATCH 071/107] fix: fix aws profile config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 20 ++++++++++++++++++++ internal/gitlab/gitlab.go | 2 +- internal/vault/vault.go | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index ac8d60798..fe2546fc2 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -253,6 +253,26 @@ to quickly create a Cobra application.`, if !skipVault { log.Println("waiting for vault unseal") + /** + + */ + + x = 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "pod", "-l", "vault-initialized=true", "-n", "vault") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + if err != nil { + log.Println("Waiting vault to be born") + time.Sleep(10 * time.Second) + } else { + log.Println("a Pod was found, continuing") + time.Sleep(25 * time.Second) + break + } + } + waitForVaultUnseal(config) log.Println("vault unseal condition met - continuing") diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index be29f1cd6..c4dd3e050 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -252,7 +252,7 @@ func ApplyGitlabTerraform(dryRun bool, directory string) { //* AWS_SDK_LOAD_CONFIG=1 //* https://registry.terraform.io/providers/hashicorp/aws/2.34.0/docs#shared-credentials-file os.Setenv("AWS_SDK_LOAD_CONFIG", "1") - os.Setenv("AWS_PROFILE", "starter") // todo this is an issue + os.Setenv("AWS_PROFILE", config.AwsProfile) // Prepare for terraform gitlab execution os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) os.Setenv("GITLAB_BASE_URL", viper.GetString("gitlab.local.service")) diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 68217e46e..44feff0bd 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -90,7 +90,7 @@ func ConfigureVault(dryRun bool) { os.Setenv("VAULT_ADDR", "http://localhost:8200") os.Setenv("VAULT_TOKEN", vaultToken) os.Setenv("AWS_SDK_LOAD_CONFIG", "1") - os.Setenv("AWS_PROFILE", "starter") // todo this is an issue + os.Setenv("AWS_PROFILE", config.AwsProfile) os.Setenv("AWS_DEFAULT_REGION", viper.GetString("aws.region")) os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename"))) From f325a13fb5e6a51840f2e7de0fec34c8c9bf8cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Tue, 12 Jul 2022 17:52:47 -0300 Subject: [PATCH 072/107] chore: expose gitlab and vault ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- docker-compose.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yaml b/docker-compose.yaml index 3da69c110..8070ea994 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -9,6 +9,8 @@ services: container_name: kubefirst-dev ports: - "8080:8080" # ArgoCD + - "8888:8888" # GitLab + - "8200:8200" # Vault volumes: - ./:/home/developer/kubefirst env_file: From abb43520ba9fe50ae01446b2fdb914e92e0c865c Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 13 Jul 2022 16:50:43 +0000 Subject: [PATCH 073/107] Adding new ways to call shell Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/gitlab/gitlab.go | 53 +++++++++++-------------- pkg/shell.go | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 31 deletions(-) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index c4dd3e050..978bdc8cb 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -251,30 +251,25 @@ func ApplyGitlabTerraform(dryRun bool, directory string) { } //* AWS_SDK_LOAD_CONFIG=1 //* https://registry.terraform.io/providers/hashicorp/aws/2.34.0/docs#shared-credentials-file - os.Setenv("AWS_SDK_LOAD_CONFIG", "1") - os.Setenv("AWS_PROFILE", config.AwsProfile) + envs := map[string]string{} + envs["AWS_SDK_LOAD_CONFIG"]="1" + envs["AWS_PROFILE"]=config.AwsProfile // Prepare for terraform gitlab execution - os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) - os.Setenv("GITLAB_BASE_URL", viper.GetString("gitlab.local.service")) + envs["GITLAB_TOKEN"]=viper.GetString("gitlab.token") + envs["GITLAB_BASE_URL"]=viper.GetString("gitlab.local.service") directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) err := os.Chdir(directory) if err != nil { log.Panic("error: could not change directory to " + directory) } - tfInitCmd := exec.Command(config.TerraformPath, "init") - tfInitCmd.Stdout = os.Stdout - tfInitCmd.Stderr = os.Stderr - err = tfInitCmd.Run() - if err != nil { + tfInitCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + if tfInitCmdErr != nil { log.Panicf("error: terraform init for gitlab failed %s", err) } - tfApplyCmd := exec.Command(config.TerraformPath, "apply", "-auto-approve") - tfApplyCmd.Stdout = os.Stdout - tfApplyCmd.Stderr = os.Stderr - err = tfApplyCmd.Run() - if err != nil { + tfApplyCmdErr := pkg.ExecShellWithVars(envs, config.TerraformPath, "apply", "-auto-approve") + if tfApplyCmdErr != nil { log.Panicf("error: terraform apply for gitlab failed %s", err) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) @@ -328,15 +323,17 @@ func GitlabKeyUpload(dryRun bool) { func DestroyGitlabTerraform(skipGitlabTerraform bool) { config := configs.ReadConfig() + envs := map[string]string{} - os.Setenv("AWS_REGION", viper.GetString("aws.region")) - os.Setenv("AWS_ACCOUNT_ID", viper.GetString("aws.accountid")) - os.Setenv("HOSTED_ZONE_NAME", viper.GetString("aws.hostedzonename")) - os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) + envs["AWS_REGION"]= viper.GetString("aws.region") + envs["AWS_ACCOUNT_ID"]= viper.GetString("aws.accountid") + envs["HOSTED_ZONE_NAME"]= viper.GetString("aws.hostedzonename") + envs["GITLAB_TOKEN"]= viper.GetString("gitlab.token") + + envs["TF_VAR_aws_account_id"]= viper.GetString("aws.accountid") + envs["TF_VAR_aws_region"]= viper.GetString("aws.region") + envs["TF_VAR_hosted_zone_name"]= viper.GetString("aws.hostedzonename") - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) err := os.Chdir(directory) @@ -347,19 +344,13 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { os.Setenv("GITLAB_BASE_URL", viper.GetString("gitlab.local.service")) if !skipGitlabTerraform { - tfInitGitlabCmd := exec.Command(config.TerraformPath, "init") - tfInitGitlabCmd.Stdout = os.Stdout - tfInitGitlabCmd.Stderr = os.Stderr - err = tfInitGitlabCmd.Run() - if err != nil { + tfInitGitlabCmdErr := pkg.ExecShellWithVars(envs, config.TerraformPath, "init") + if tfInitGitlabCmdErr != nil { log.Panicf("failed to terraform init gitlab %s", err) } - tfDestroyGitlabCmd := exec.Command(config.TerraformPath, "destroy", "-auto-approve") - tfDestroyGitlabCmd.Stdout = os.Stdout - tfDestroyGitlabCmd.Stderr = os.Stderr - err = tfDestroyGitlabCmd.Run() - if err != nil { + tfDestroyGitlabCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") + if tfDestroyGitlabCmdErr != nil { log.Panicf("failed to terraform destroy gitlab %s", err) } diff --git a/pkg/shell.go b/pkg/shell.go index f44337ea6..d12f4c2c1 100644 --- a/pkg/shell.go +++ b/pkg/shell.go @@ -2,8 +2,11 @@ package pkg import ( "bytes" + "fmt" "log" + "os" "os/exec" + "bufio" ) func ExecShellReturnStrings(command string, args ...string) (string, string, error) { @@ -20,3 +23,82 @@ func ExecShellReturnStrings(command string, args ...string) (string, string, err log.Println("Commad Execution STDERR: %s", errb.String()) return outb.String(), errb.String(), err } + +func ExecShellReturnStringsWithVars(osvars map[string]string, command string, args ...string) (string, string, error) { + + log.Printf("INFO: Running %s",command) + for k, v := range osvars { + os.Setenv(k, v) + log.Printf(" export %s = %s", k, v) + } + var outb, errb bytes.Buffer + k := exec.Command(command, args...) + k.Stdout = &outb + k.Stderr = &errb + err := k.Run() + if err != nil { + log.Println("Error executing command: %v", err) + } + log.Println("Commad Execution: %s", command) + log.Println("Commad Execution STDOUT: %s", outb.String()) + log.Println("Commad Execution STDERR: %s", errb.String()) + return outb.String(), errb.String(), err +} + +// Exec shell actions supporting: +// - On-the-fly logging of result +// - Map of Vars loaded +func ExecShellWithVars(osvars map[string]string, command string, args ...string) (error) { + + + log.Printf("INFO: Running %s",command) + for k, v := range osvars { + os.Setenv(k, v) + log.Printf(" export %s = %s", k, v) + } + cmd := exec.Command(command, args...) + cmdReaderOut, _ := cmd.StdoutPipe() + cmdReaderErr, _ := cmd.StderrPipe() + + scannerOut := bufio.NewScanner(cmdReaderOut) + stdOut := make(chan string) + go reader(scannerOut, stdOut) + doneOut := make(chan bool) + + scannerErr := bufio.NewScanner(cmdReaderErr) + stdErr := make(chan string) + go reader(scannerErr, stdErr) + doneErr := make(chan bool) + go func() { + for msg := range stdOut { + log.Println("OUT: ",msg) + } + doneOut <- true + }() + go func() { + for msg := range stdErr { + log.Println("ERR: ",msg) + } + doneErr <- true + }() + + errApply := cmd.Run() + if errApply != nil { + log.Println(fmt.Sprintf("error: %s init failed %v",command, errApply)) + return errApply + } else { + close(stdOut) + close(stdErr) + } + <-doneOut + <-doneErr + return nil + +} + +// Not meant to be exported, for internal use only. +func reader(scanner *bufio.Scanner, out chan string) { + for scanner.Scan() { + out <- scanner.Text() + } +} From e938e4113779263e2d2c32fc35214e642ad27944 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 13 Jul 2022 17:11:44 +0000 Subject: [PATCH 074/107] Migrate most of terraforms Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/gitlab/gitlab.go | 8 ++--- internal/terraform/terraform.go | 50 +++++++++++++------------------ internal/vault/vault.go | 52 +++++++++++++++------------------ 3 files changed, 48 insertions(+), 62 deletions(-) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 978bdc8cb..473a700fd 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -265,12 +265,12 @@ func ApplyGitlabTerraform(dryRun bool, directory string) { } tfInitCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") if tfInitCmdErr != nil { - log.Panicf("error: terraform init for gitlab failed %s", err) + log.Panicf("error: terraform init for gitlab failed %s", tfInitCmdErr) } tfApplyCmdErr := pkg.ExecShellWithVars(envs, config.TerraformPath, "apply", "-auto-approve") if tfApplyCmdErr != nil { - log.Panicf("error: terraform apply for gitlab failed %s", err) + log.Panicf("error: terraform apply for gitlab failed %s", tfApplyCmdErr) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) viper.Set("create.terraformapplied.gitlab", true) @@ -346,12 +346,12 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { if !skipGitlabTerraform { tfInitGitlabCmdErr := pkg.ExecShellWithVars(envs, config.TerraformPath, "init") if tfInitGitlabCmdErr != nil { - log.Panicf("failed to terraform init gitlab %s", err) + log.Panicf("failed to terraform init gitlab %s", tfInitGitlabCmdErr) } tfDestroyGitlabCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") if tfDestroyGitlabCmdErr != nil { - log.Panicf("failed to terraform destroy gitlab %s", err) + log.Panicf("failed to terraform destroy gitlab %s", tfDestroyGitlabCmdErr) } viper.Set("destroy.terraformdestroy.gitlab", true) diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index 90da44e7d..5a49f120b 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -21,27 +21,22 @@ func ApplyBaseTerraform(dryRun bool, directory string) { log.Printf("[#99] Dry-run mode, applyBaseTerraform skipped.") return } - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) + envs := map[string]string{} + envs["TF_VAR_aws_account_id"]=viper.GetString("aws.accountid") + envs["TF_VAR_aws_region"]= viper.GetString("aws.region") + envs["TF_VAR_hosted_zone_name"]=viper.GetString("aws.hostedzonename") err := os.Chdir(directory) if err != nil { log.Panicf("error, directory does not exist - did you `kubefirst init`?: %s \nerror: %v", directory, err) } - terraformInit := exec.Command(config.TerraformPath, "init") - terraformInit.Stdout = os.Stdout - terraformInit.Stderr = os.Stderr - errInit := terraformInit.Run() - if errInit != nil { - log.Panic(fmt.Sprintf("error: terraform init failed %v", errInit)) + terraformInitErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + if terraformInitErr != nil { + log.Panic(fmt.Sprintf("error: terraform init failed %v", terraformInitErr)) } - terraformApply := exec.Command(config.TerraformPath, "apply", "-auto-approve") - terraformApply.Stdout = os.Stdout - terraformApply.Stderr = os.Stderr - errApply := terraformApply.Run() - if errApply != nil { - log.Panic(fmt.Sprintf("error: terraform init failed %v", errApply)) + terraformApplyErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "apply", "-auto-approve") + if terraformApplyErr != nil { + log.Panic(fmt.Sprintf("error: terraform init failed %v", terraformApplyErr)) } var keyOut bytes.Buffer @@ -74,24 +69,19 @@ func DestroyBaseTerraform(skipBaseTerraform bool) { log.Panicf("error: could not change directory to " + directory) } - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) + envs := map[string]string{} + envs["TF_VAR_aws_account_id"]=viper.GetString("aws.accountid") + envs["TF_VAR_aws_region"]= viper.GetString("aws.region") + envs["TF_VAR_hosted_zone_name"]=viper.GetString("aws.hostedzonename") - terraformInit := exec.Command(config.TerraformPath, "init") - terraformInit.Stdout = os.Stdout - terraformInit.Stderr = os.Stderr - errInit := terraformInit.Run() - if errInit != nil { - log.Panicf("failed to terraform init base %v", errInit) + terraformInitErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + if terraformInitErr != nil { + log.Panicf("failed to terraform init base %v", terraformInitErr) } - terraformDestroy := exec.Command(config.TerraformPath, "destroy", "-auto-approve") - terraformDestroy.Stdout = os.Stdout - terraformDestroy.Stderr = os.Stderr - errDestroy := terraformDestroy.Run() - if errDestroy != nil { - log.Panicf("failed to terraform destroy base %v", errDestroy) + terraformDestroyErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") + if terraformDestroyErr != nil { + log.Panicf("failed to terraform destroy base %v", terraformDestroyErr) } viper.Set("destroy.terraformdestroy.base", true) viper.WriteConfig() diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 44feff0bd..1a0b2a7e7 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -6,6 +6,7 @@ import ( "fmt" vault "github.com/hashicorp/vault/api" "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/pkg" "github.com/kubefirst/kubefirst/internal/k8s" "github.com/spf13/viper" gitlab "github.com/xanzy/go-gitlab" @@ -87,22 +88,23 @@ func ConfigureVault(dryRun bool) { } // Prepare for terraform vault execution - os.Setenv("VAULT_ADDR", "http://localhost:8200") - os.Setenv("VAULT_TOKEN", vaultToken) - os.Setenv("AWS_SDK_LOAD_CONFIG", "1") - os.Setenv("AWS_PROFILE", config.AwsProfile) - os.Setenv("AWS_DEFAULT_REGION", viper.GetString("aws.region")) - - os.Setenv("TF_VAR_vault_addr", fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename"))) - os.Setenv("TF_VAR_aws_account_id", viper.GetString("aws.accountid")) - os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) - os.Setenv("TF_VAR_email_address", viper.GetString("adminemail")) - os.Setenv("TF_VAR_gitlab_runner_token", viper.GetString("gitlab.runnertoken")) - os.Setenv("TF_VAR_gitlab_token", viper.GetString("gitlab.token")) - os.Setenv("TF_VAR_hosted_zone_id", viper.GetString("aws.domainid")) - os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - os.Setenv("TF_VAR_vault_token", viper.GetString("aws.hostedzonename")) - os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") + envs := map[string]string{} + envs["VAULT_ADDR"]="http://localhost:8200" //Should this come from init? + envs["VAULT_TOKEN"]=vaultToken + envs["AWS_SDK_LOAD_CONFIG"]= "1" + envs["AWS_PROFILE"]=config.AwsProfile + envs["AWS_DEFAULT_REGION"]= viper.GetString("aws.region") + + envs["TF_VAR_vault_addr"]= fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename")) + envs["TF_VAR_aws_account_id"]= viper.GetString("aws.accountid") + envs["TF_VAR_aws_region"]= viper.GetString("aws.region") + envs["TF_VAR_email_address"]= viper.GetString("adminemail") + envs["TF_VAR_gitlab_runner_token"]= viper.GetString("gitlab.runnertoken") + envs["TF_VAR_gitlab_token"]= viper.GetString("gitlab.token") + envs["TF_VAR_hosted_zone_id"]= viper.GetString("aws.domainid") + envs["TF_VAR_hosted_zone_name"]= viper.GetString("aws.hostedzonename") + envs["TF_VAR_vault_token"]= viper.GetString("aws.hostedzonename") //! todo: probably wrong value + envs["TF_VAR_vault_redirect_uris"]= "[\"will-be-patched-later\"]" directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", config.HomePath) err = os.Chdir(directory) @@ -110,20 +112,14 @@ func ConfigureVault(dryRun bool) { log.Panicf("error: could not change directory to " + directory) } - tfInitCmd := exec.Command(config.TerraformPath, "init") - tfInitCmd.Stdout = os.Stdout - tfInitCmd.Stderr = os.Stderr - err = tfInitCmd.Run() - if err != nil { - log.Panicf("error: terraform init failed %s", err) + tfInitCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + if tfInitCmdErr != nil { + log.Panicf("error: terraform init failed %s", tfInitCmdErr) } - tfApplyCmd := exec.Command(config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") - tfApplyCmd.Stdout = os.Stdout - tfApplyCmd.Stderr = os.Stderr - err = tfApplyCmd.Run() - if err != nil { - log.Panicf("error: terraform apply failed %s", err) + tfApplyCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") + if tfApplyCmdErr != nil { + log.Panicf("error: terraform apply failed %s", tfApplyCmdErr) } viper.Set("create.terraformapplied.vault", true) From d46a034f5fe69da541ecbdf236408eabc4fb10a2 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 13 Jul 2022 17:16:01 +0000 Subject: [PATCH 075/107] Fix Var Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/vault/vault.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 1a0b2a7e7..5ee4d18b3 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -103,7 +103,7 @@ func ConfigureVault(dryRun bool) { envs["TF_VAR_gitlab_token"]= viper.GetString("gitlab.token") envs["TF_VAR_hosted_zone_id"]= viper.GetString("aws.domainid") envs["TF_VAR_hosted_zone_name"]= viper.GetString("aws.hostedzonename") - envs["TF_VAR_vault_token"]= viper.GetString("aws.hostedzonename") //! todo: probably wrong value + envs["TF_VAR_vault_token"]= vaultToken envs["TF_VAR_vault_redirect_uris"]= "[\"will-be-patched-later\"]" directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", config.HomePath) From a91d3de02b6e05293e5735cedf019a6e87503f42 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 13 Jul 2022 18:03:31 +0000 Subject: [PATCH 076/107] Fix err Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/gitlab/gitlab.go | 24 ++++++++++++------------ internal/terraform/terraform.go | 24 ++++++++++++------------ internal/vault/vault.go | 12 ++++++------ 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 473a700fd..6811306fe 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -263,14 +263,14 @@ func ApplyGitlabTerraform(dryRun bool, directory string) { if err != nil { log.Panic("error: could not change directory to " + directory) } - tfInitCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") - if tfInitCmdErr != nil { - log.Panicf("error: terraform init for gitlab failed %s", tfInitCmdErr) + err = pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + if err != nil { + log.Panicf("error: terraform init for gitlab failed %s", err) } - tfApplyCmdErr := pkg.ExecShellWithVars(envs, config.TerraformPath, "apply", "-auto-approve") - if tfApplyCmdErr != nil { - log.Panicf("error: terraform apply for gitlab failed %s", tfApplyCmdErr) + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "apply", "-auto-approve") + if err != nil { + log.Panicf("error: terraform apply for gitlab failed %s", err) } os.RemoveAll(fmt.Sprintf("%s/.terraform", directory)) viper.Set("create.terraformapplied.gitlab", true) @@ -344,14 +344,14 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { os.Setenv("GITLAB_BASE_URL", viper.GetString("gitlab.local.service")) if !skipGitlabTerraform { - tfInitGitlabCmdErr := pkg.ExecShellWithVars(envs, config.TerraformPath, "init") - if tfInitGitlabCmdErr != nil { - log.Panicf("failed to terraform init gitlab %s", tfInitGitlabCmdErr) + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "init") + if err != nil { + log.Panicf("failed to terraform init gitlab %s", err) } - tfDestroyGitlabCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") - if tfDestroyGitlabCmdErr != nil { - log.Panicf("failed to terraform destroy gitlab %s", tfDestroyGitlabCmdErr) + err = pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") + if err != nil { + log.Panicf("failed to terraform destroy gitlab %s", err) } viper.Set("destroy.terraformdestroy.gitlab", true) diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index 5a49f120b..86ac83585 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -30,13 +30,13 @@ func ApplyBaseTerraform(dryRun bool, directory string) { if err != nil { log.Panicf("error, directory does not exist - did you `kubefirst init`?: %s \nerror: %v", directory, err) } - terraformInitErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") - if terraformInitErr != nil { - log.Panic(fmt.Sprintf("error: terraform init failed %v", terraformInitErr)) + err = pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + if err != nil { + log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) } - terraformApplyErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "apply", "-auto-approve") - if terraformApplyErr != nil { - log.Panic(fmt.Sprintf("error: terraform init failed %v", terraformApplyErr)) + err = pkg.ExecShellWithVars(envs,config.TerraformPath, "apply", "-auto-approve") + if err != nil { + log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) } var keyOut bytes.Buffer @@ -74,14 +74,14 @@ func DestroyBaseTerraform(skipBaseTerraform bool) { envs["TF_VAR_aws_region"]= viper.GetString("aws.region") envs["TF_VAR_hosted_zone_name"]=viper.GetString("aws.hostedzonename") - terraformInitErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") - if terraformInitErr != nil { - log.Panicf("failed to terraform init base %v", terraformInitErr) + err = pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + if err != nil { + log.Panicf("failed to terraform init base %v", err) } - terraformDestroyErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") - if terraformDestroyErr != nil { - log.Panicf("failed to terraform destroy base %v", terraformDestroyErr) + err = pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") + if err != nil { + log.Panicf("failed to terraform destroy base %v", err) } viper.Set("destroy.terraformdestroy.base", true) viper.WriteConfig() diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 5ee4d18b3..8ec0411f1 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -112,14 +112,14 @@ func ConfigureVault(dryRun bool) { log.Panicf("error: could not change directory to " + directory) } - tfInitCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "init") - if tfInitCmdErr != nil { - log.Panicf("error: terraform init failed %s", tfInitCmdErr) + err = pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + if err != nil { + log.Panicf("error: terraform init failed %s", err) } - tfApplyCmdErr := pkg.ExecShellWithVars(envs,config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") - if tfApplyCmdErr != nil { - log.Panicf("error: terraform apply failed %s", tfApplyCmdErr) + err = pkg.ExecShellWithVars(envs,config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") + if err != nil { + log.Panicf("error: terraform apply failed %s", err) } viper.Set("create.terraformapplied.vault", true) From ce0b930638ebb4ae8d56a37f85568f052c1e89c6 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 13 Jul 2022 18:14:35 +0000 Subject: [PATCH 077/107] Fix err Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- pkg/shell.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pkg/shell.go b/pkg/shell.go index d12f4c2c1..253cfa792 100644 --- a/pkg/shell.go +++ b/pkg/shell.go @@ -24,6 +24,8 @@ func ExecShellReturnStrings(command string, args ...string) (string, string, err return outb.String(), errb.String(), err } +// Exec shell actions supporting: +// - Map of Vars loaded func ExecShellReturnStringsWithVars(osvars map[string]string, command string, args ...string) (string, string, error) { log.Printf("INFO: Running %s",command) @@ -57,8 +59,16 @@ func ExecShellWithVars(osvars map[string]string, command string, args ...string) log.Printf(" export %s = %s", k, v) } cmd := exec.Command(command, args...) - cmdReaderOut, _ := cmd.StdoutPipe() - cmdReaderErr, _ := cmd.StderrPipe() + cmdReaderOut, err := cmd.StdoutPipe() + if err != nil { + log.Println(fmt.Sprintf("error: %s failed creating out pipe for: %v",command, err)) + return err + } + cmdReaderErr, err := cmd.StderrPipe() + if err != nil { + log.Println(fmt.Sprintf("error: %s failed creating out pipe for: %v",command, err)) + return err + } scannerOut := bufio.NewScanner(cmdReaderOut) stdOut := make(chan string) @@ -82,10 +92,10 @@ func ExecShellWithVars(osvars map[string]string, command string, args ...string) doneErr <- true }() - errApply := cmd.Run() - if errApply != nil { - log.Println(fmt.Sprintf("error: %s init failed %v",command, errApply)) - return errApply + err = cmd.Run() + if err != nil { + log.Println(fmt.Sprintf("error: %s failed %v",command, err)) + return err } else { close(stdOut) close(stdErr) From 6736de778232061b3b3330da9ec8dd77cf6458cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 13 Jul 2022 15:18:02 -0300 Subject: [PATCH 078/107] feat: update files and folders to follow rebranding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/clean.go | 22 ++++++++++++++------ cmd/create.go | 2 +- cmd/info.go | 7 ++++--- cmd/kubefirstTemplate.go | 20 +++++++++--------- cmd/root.go | 2 +- configs/config.go | 31 +++++++++++++++++----------- configs/flareFile.go | 20 ------------------ configs/kubefirstDirectory.go | 10 ++++----- configs/kubefirstFile.go | 20 ++++++++++++++++++ internal/downloadManager/download.go | 8 +++---- internal/gitClient/git.go | 6 +++--- internal/gitlab/gitlab.go | 14 ++++++------- internal/helm/helm.go | 2 +- internal/softserve/softserve.go | 6 +++--- internal/terraform/terraform.go | 10 ++++----- internal/vault/vault.go | 2 +- pkg/keys.go | 2 +- 17 files changed, 101 insertions(+), 83 deletions(-) delete mode 100755 configs/flareFile.go create mode 100755 configs/kubefirstFile.go diff --git a/cmd/clean.go b/cmd/clean.go index 052dd24cd..3e86f779f 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -26,15 +26,25 @@ to quickly create a Cobra application.`, log.Println("removing $HOME/.kubefirst and $HOME/.flare") // todo ask for user input to verify? - os.RemoveAll(fmt.Sprintf("%s/.kubefirst", config.HomePath)) - os.Remove(fmt.Sprintf("%s/.flare", config.HomePath)) + err := os.RemoveAll(config.K1srtFolderPath) + if err != nil { + log.Panicf("unable to delete %q folder, error is: %s", config.K1srtFolderPath, err) + } + + err = os.Remove(config.KubefirstConfigFilePath) + if err != nil { + log.Panicf("unable to delete %q file, error is: ", err) + } log.Println("removed $HOME/.kubefirst and $HOME/.flare") - if err := os.Mkdir(fmt.Sprintf("%s/.kubefirst", config.HomePath), os.ModePerm); err != nil { - log.Panicf("error: could not create directory $HOME/.kubefirst - it must exist to continue %s", err) + + log.Printf("%q and %q folders were removed", config.K1srtFolderPath, config.KubectlClientPath) + + if err := os.Mkdir(fmt.Sprintf("%s", config.K1srtFolderPath), os.ModePerm); err != nil { + log.Panicf("error: could not create directory %q - it must exist to continue. error is: %s", config.K1srtFolderPath, err) } - toolsDir := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) + toolsDir := fmt.Sprintf("%s/tools", config.K1srtFolderPath) if err := os.Mkdir(toolsDir, os.ModePerm); err != nil { - log.Panicf("error: could not create directory $HOME/.kubefirst/tools - it must exist to continue %s", err) + log.Panicf("error: could not create directory %q/tools - it must exist to continue %s", config.K1srtFolderPath, err) } log.Println("created $HOME/.kubefirst and $HOME/.kubefirst/tools - proceed to `kubefirst init`") diff --git a/cmd/create.go b/cmd/create.go index fe2546fc2..151b02055 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -73,7 +73,7 @@ to quickly create a Cobra application.`, log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) + directory := fmt.Sprintf("%s/gitops/terraform/base", config.K1srtFolderPath) terraform.ApplyBaseTerraform(dryRun, directory) Trackers[trackerStage20].Tracker.Increment(int64(1)) diff --git a/cmd/info.go b/cmd/info.go index 6b10e0064..ad64f6a6f 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -21,16 +21,17 @@ var infoCmd = &cobra.Command{ fmt.Printf("Operational System: %s\n", config.LocalOs) fmt.Printf("Architecture: %s\n", config.LocalArchitecture) fmt.Printf("Go Lang version: v%s \n", runtime.Version()) - fmt.Printf("$HOME folder: %s\n", config.HomePath) + fmt.Printf("Kubefirst config file: %s\n", config.KubefirstConfigFilePath) + fmt.Printf("Kubefirst config folder: %s\n", config.K1srtFolderPath) fmt.Printf("Kubectl path: %s\n", config.KubectlClientPath) fmt.Printf("Terraform path: %s\n", config.TerraformPath) fmt.Printf("Kubeconfig path: %s\n", config.KubeConfigPath) - err := configs.CheckFlareFile(config.HomePath) + err := configs.CheckKubefirstConfigFile(config) if err != nil { log.Panic(err) } - err = configs.CheckKubefirstDir(config.HomePath) + err = configs.CheckKubefirstDir(config) if err != nil { log.Panic(err) } diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 92fe12f66..897dac99d 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -2,18 +2,18 @@ package cmd import ( "fmt" + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" "github.com/kubefirst/kubefirst/configs" + "github.com/spf13/viper" "io/ioutil" "log" "os" "path/filepath" "strings" "time" - "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/object" - "github.com/go-git/go-git/v5" - gitConfig "github.com/go-git/go-git/v5/config" - "github.com/spf13/viper" ) func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName string, branch string) { @@ -23,12 +23,12 @@ func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName st } repoUrl := fmt.Sprintf("https://github.com/%s/%s-template", githubOrg, repoName) - directory := fmt.Sprintf("%s/.kubefirst/%s", config.HomePath, repoName) + directory := fmt.Sprintf("%s/%s", config.K1srtFolderPath, repoName) log.Println("git clone", repoUrl, directory) log.Println("git clone -b ", branch, repoUrl, directory) repo, err := git.PlainClone(directory, false, &git.CloneOptions{ - URL: repoUrl, + URL: repoUrl, ReferenceName: plumbing.NewBranchReferenceName(branch), SingleBranch: true, }) @@ -38,11 +38,11 @@ func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName st viper.Set(fmt.Sprintf("init.repos.%s.cloned", repoName), true) viper.WriteConfig() - log.Printf("cloned %s-template repository to directory %s/.kubefirst/%s", repoName, config.HomePath, repoName) + log.Printf("cloned %s-template repository to directory %s/%s", repoName, config.K1srtFolderPath, repoName) - log.Printf("detokenizing %s/.kubefirst/%s", config.HomePath, repoName) + log.Printf("detokenizing %s/%s", config.K1srtFolderPath, repoName) detokenize(directory) - log.Printf("detokenization of %s/.kubefirst/%s complete", config.HomePath, repoName) + log.Printf("detokenization of %s/%s complete", config.K1srtFolderPath, repoName) viper.Set(fmt.Sprintf("init.repos.%s.detokenized", repoName), true) viper.WriteConfig() diff --git a/cmd/root.go b/cmd/root.go index b2f2e3eb8..39f146443 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -50,7 +50,7 @@ func init() { func initConfig() { config := configs.ReadConfig() if cfgFile == "" { - cfgFile = config.HomePath + "/.flare" + cfgFile = config.KubefirstConfigFilePath } if _, err := os.Stat(cfgFile); errors.Is(err, os.ErrNotExist) { diff --git a/configs/config.go b/configs/config.go index 1933f34b6..06b0de70c 100644 --- a/configs/config.go +++ b/configs/config.go @@ -21,12 +21,13 @@ type Config struct { LocalArchitecture string InstallerEmail string - KubefirstLogPath string `env:"KUBEFIRST_LOG_PATH" envDefault:"logs"` - HomePath string - KubectlClientPath string - KubeConfigPath string - HelmClientPath string - TerraformPath string + KubefirstLogPath string `env:"KUBEFIRST_LOG_PATH" envDefault:"logs"` + KubefirstConfigFilePath string + K1srtFolderPath string + KubectlClientPath string + KubeConfigPath string + HelmClientPath string + TerraformPath string KubectlVersion string `env:"KUBECTL_VERSION" envDefault:"v1.20.0"` TerraformVersion string @@ -44,19 +45,25 @@ func ReadConfig() *Config { log.Panic(err) } - var err error - config.HomePath, err = os.UserHomeDir() + homePath, err := os.UserHomeDir() if err != nil { log.Panic(err) } + config.K1srtFolderPath = fmt.Sprintf("%s/.k1srt", homePath) + if err != nil { + log.Panic(err) + } + + config.KubefirstConfigFilePath = fmt.Sprintf("%s/.kubefirst", homePath) + config.LocalOs = runtime.GOOS config.LocalArchitecture = runtime.GOARCH - config.KubectlClientPath = fmt.Sprintf("%s/.kubefirst/tools/kubectl", config.HomePath) - config.KubeConfigPath = fmt.Sprintf("%s/.kubefirst/gitops/terraform/base/kubeconfig_kubefirst", config.HomePath) - config.TerraformPath = fmt.Sprintf("%s/.kubefirst/tools/terraform", config.HomePath) - config.HelmClientPath = fmt.Sprintf("%s/.kubefirst/tools/helm", config.HomePath) + config.KubectlClientPath = fmt.Sprintf("%s/tools/kubectl", config.K1srtFolderPath) + config.KubeConfigPath = fmt.Sprintf("%s/gitops/terraform/base/kubeconfig_kubefirst", config.K1srtFolderPath) + config.TerraformPath = fmt.Sprintf("%s/tools/terraform", config.K1srtFolderPath) + config.HelmClientPath = fmt.Sprintf("%s/tools/helm", config.K1srtFolderPath) config.TerraformVersion = "1.0.11" diff --git a/configs/flareFile.go b/configs/flareFile.go deleted file mode 100755 index 6229dbd0b..000000000 --- a/configs/flareFile.go +++ /dev/null @@ -1,20 +0,0 @@ -package configs - -import ( - "fmt" - "log" - "os" -) - -// CheckFlareFile validate if ~/.flare file is ready to be consumed. -func CheckFlareFile(home string) error { - flareFile := fmt.Sprintf("%s/.flare", home) - if _, err := os.Stat(flareFile); err != nil { - errorMsg := fmt.Sprintf("unable to load \".flare\" file, error is: %s", err) - log.Println(errorMsg) - return fmt.Errorf(errorMsg) - } - - log.Println(".flare file is set") - return nil -} \ No newline at end of file diff --git a/configs/kubefirstDirectory.go b/configs/kubefirstDirectory.go index 2942c66e5..87895b40f 100755 --- a/configs/kubefirstDirectory.go +++ b/configs/kubefirstDirectory.go @@ -6,15 +6,15 @@ import ( "os" ) -// CheckKubefirstDir validate if ~/.kubefirst directory is ready to be used -func CheckKubefirstDir(home string) error { - k1sDir := fmt.Sprintf("%s/.kubefirst", home) +// CheckKubefirstDir validate if ~/.k1srt directory is ready to be used +func CheckKubefirstDir(config *Config) error { + k1sDir := fmt.Sprintf("%s", config.K1srtFolderPath) if _, err := os.Stat(k1sDir); err != nil { - errorMsg := fmt.Sprintf("unable to load \".kubefirst\" directory, error is: %s", err) + errorMsg := fmt.Sprintf("unable to load \".k1srt\" directory, error is: %s", err) log.Println(errorMsg) return fmt.Errorf(errorMsg) } - log.Printf("\".kubefirst\" directory found: %s", k1sDir) + log.Printf("\".k1srt\" directory found: %s", k1sDir) return nil } diff --git a/configs/kubefirstFile.go b/configs/kubefirstFile.go new file mode 100755 index 000000000..baec7261b --- /dev/null +++ b/configs/kubefirstFile.go @@ -0,0 +1,20 @@ +package configs + +import ( + "fmt" + "log" + "os" +) + +// CheckKubefirstConfigFile validate if ~/.flare file is ready to be consumed. +func CheckKubefirstConfigFile(config *Config) error { + flareFile := fmt.Sprintf("%s", config.KubefirstConfigFilePath) + if _, err := os.Stat(flareFile); err != nil { + errorMsg := fmt.Sprintf("unable to load %q file, error is: %s", config.KubefirstConfigFilePath, err) + log.Println(errorMsg) + return fmt.Errorf(errorMsg) + } + + log.Printf("%q file is set", config.KubefirstConfigFilePath) + return nil +} diff --git a/internal/downloadManager/download.go b/internal/downloadManager/download.go index 7414ff69e..64d0f337c 100644 --- a/internal/downloadManager/download.go +++ b/internal/downloadManager/download.go @@ -17,7 +17,7 @@ import ( func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracker) error { - toolsDir := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) + toolsDir := fmt.Sprintf("%s/tools", config.K1srtFolderPath) err := os.Mkdir(toolsDir, 0777) if err != nil { @@ -67,14 +67,14 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke config.LocalArchitecture, ) - terraformDownloadZipPath := fmt.Sprintf("%s/.kubefirst/tools/terraform.zip", config.HomePath) + terraformDownloadZipPath := fmt.Sprintf("%s/tools/terraform.zip", config.K1srtFolderPath) err = downloadFile(terraformDownloadZipPath, terraformDownloadUrl) if err != nil { log.Println("error reading terraform file") return err } - unzipDirectory := fmt.Sprintf("%s/.kubefirst/tools", config.HomePath) + unzipDirectory := fmt.Sprintf("%s/tools", config.K1srtFolderPath) unzip(terraformDownloadZipPath, unzipDirectory) err = os.Chmod(unzipDirectory, 0777) @@ -98,7 +98,7 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke config.LocalArchitecture, ) - helmDownloadTarGzPath := fmt.Sprintf("%s/.kubefirst/tools/helm.tar.gz", config.HomePath) + helmDownloadTarGzPath := fmt.Sprintf("%s/tools/helm.tar.gz", config.K1srtFolderPath) err = downloadFile(helmDownloadTarGzPath, helmDownloadUrl) if err != nil { return err diff --git a/internal/gitClient/git.go b/internal/gitClient/git.go index db1433792..852011153 100644 --- a/internal/gitClient/git.go +++ b/internal/gitClient/git.go @@ -19,7 +19,7 @@ func CloneGitOpsRepo() { config := configs.ReadConfig() url := "https://github.com/kubefirst/gitops-template" - directory := fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath) + directory := fmt.Sprintf("%s/gitops", config.K1srtFolderPath) versionGitOps := viper.GetString("version-gitops") @@ -34,13 +34,13 @@ func CloneGitOpsRepo() { log.Panicf("error cloning gitops-template repository from github, error is: %s", err) } - log.Println("downloaded gitops repo from template to directory", config.HomePath, "/.kubefirst/gitops") + log.Println("downloaded gitops repo from template to directory", config.K1srtFolderPath, "/gitops") } func PushGitopsToSoftServe() { cfg := configs.ReadConfig() - directory := fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath) + directory := fmt.Sprintf("%s/gitops", cfg.K1srtFolderPath) log.Println("open gitClient repo", directory) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index c4dd3e050..658a260bd 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -93,8 +93,8 @@ func PushGitOpsToGitLab(dryRun bool) { //TODO: should this step to be skipped if already executed? domain := viper.GetString("aws.hostedzonename") - pkg.Detokenize(fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath)) - directory := fmt.Sprintf("%s/.kubefirst/gitops", cfg.HomePath) + pkg.Detokenize(fmt.Sprintf("%s/gitops", cfg.K1srtFolderPath)) + directory := fmt.Sprintf("%s/gitops", cfg.K1srtFolderPath) repo, err := git.PlainOpen(directory) if err != nil { @@ -257,7 +257,7 @@ func ApplyGitlabTerraform(dryRun bool, directory string) { os.Setenv("GITLAB_TOKEN", viper.GetString("gitlab.token")) os.Setenv("GITLAB_BASE_URL", viper.GetString("gitlab.local.service")) - directory = fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) + directory = fmt.Sprintf("%s/gitops/terraform/gitlab", config.K1srtFolderPath) err := os.Chdir(directory) if err != nil { log.Panic("error: could not change directory to " + directory) @@ -338,7 +338,7 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { os.Setenv("TF_VAR_aws_region", viper.GetString("aws.region")) os.Setenv("TF_VAR_hosted_zone_name", viper.GetString("aws.hostedzonename")) - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/gitlab", config.HomePath) + directory := fmt.Sprintf("%s/.gitops/terraform/gitlab", config.K1srtFolderPath) err := os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) @@ -461,7 +461,7 @@ func ChangeRegistryToGitLab(dryRun bool) { log.Panicf("error creating argocd repository connection secret %s", err) } - k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/.kubefirst/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.HomePath)) + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.K1srtFolderPath)) k.Stdout = os.Stdout k.Stderr = os.Stderr err = k.Run() @@ -485,7 +485,7 @@ func HydrateGitlabMetaphorRepo(dryRun bool) { return } - metaphorTemplateDir := fmt.Sprintf("%s/.kubefirst/metaphor", cfg.HomePath) + metaphorTemplateDir := fmt.Sprintf("%s/metaphor", cfg.K1srtFolderPath) url := "https://github.com/kubefirst/metaphor-template" @@ -543,7 +543,7 @@ func HydrateGitlabMetaphorRepo(dryRun bool) { // refactor: review it func PushGitRepo(config *configs.Config, gitOrigin, repoName string) { - repoDir := fmt.Sprintf("%s/.kubefirst/%s", config.HomePath, repoName) + repoDir := fmt.Sprintf("%s/%s", config.K1srtFolderPath, repoName) repo, err := git.PlainOpen(repoDir) if err != nil { log.Panicf("error opening repo %s: %s", repoName, err) diff --git a/internal/helm/helm.go b/internal/helm/helm.go index aff58b71e..9754a972a 100644 --- a/internal/helm/helm.go +++ b/internal/helm/helm.go @@ -33,7 +33,7 @@ func InstallArgocd(dryRun bool) { log.Panicf("error: could not helm repo update %s", err) } - helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", config.HomePath), "argo/argo-cd") + helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/argocd-init-values.yaml", config.K1srtFolderPath), "argo/argo-cd") helmInstallArgocdCmd.Stdout = os.Stdout helmInstallArgocdCmd.Stderr = os.Stderr err = helmInstallArgocdCmd.Run() diff --git a/internal/softserve/softserve.go b/internal/softserve/softserve.go index 0662c6052..afd85672f 100644 --- a/internal/softserve/softserve.go +++ b/internal/softserve/softserve.go @@ -24,7 +24,7 @@ func CreateSoftServe(dryRun bool, kubeconfigPath string) { return } - softServePath := fmt.Sprintf("%s/.kubefirst/gitops/components/soft-serve/manifests.yaml", config.HomePath) + softServePath := fmt.Sprintf("%s/gitops/components/soft-serve/manifests.yaml", config.K1srtFolderPath) softServeApplyOut, softServeApplyErr, errSoftServeApply := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") log.Printf("Result:\n\t%s\n\t%s\n", softServeApplyOut, softServeApplyErr) if errSoftServeApply != nil { @@ -68,7 +68,7 @@ func configureSoftServe() { config := configs.ReadConfig() url := "ssh://127.0.0.1:8022/config" - directory := fmt.Sprintf("%s/.kubefirst/config", config.HomePath) + directory := fmt.Sprintf("%s/config", config.K1srtFolderPath) log.Println("gitClient clone", url, directory) @@ -96,7 +96,7 @@ func configureSoftServe() { log.Panic(err) } - println("re-wrote config.yaml", config.HomePath, "/.kubefirst/config") + println("re-wrote config.yaml", config.K1srtFolderPath, "/config") w, _ := repo.Worktree() diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index 90da44e7d..6ec7ccda3 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -1,15 +1,15 @@ package terraform import ( + "bytes" "fmt" "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" "log" "os" - "strings" - "bytes" "os/exec" + "strings" ) func ApplyBaseTerraform(dryRun bool, directory string) { @@ -43,7 +43,7 @@ func ApplyBaseTerraform(dryRun bool, directory string) { if errApply != nil { log.Panic(fmt.Sprintf("error: terraform init failed %v", errApply)) } - + var keyOut bytes.Buffer k := exec.Command(config.TerraformPath, "output", "vault_unseal_kms_key") k.Stdout = &keyOut @@ -59,7 +59,7 @@ func ApplyBaseTerraform(dryRun bool, directory string) { viper.Set("vault.kmskeyid", keyId) viper.Set("create.terraformapplied.base", true) viper.WriteConfig() - pkg.Detokenize(fmt.Sprintf("%s/.kubefirst/gitops", config.HomePath)) + pkg.Detokenize(fmt.Sprintf("%s/gitops", config.K1srtFolderPath)) } else { log.Println("Skipping: ApplyBaseTerraform") } @@ -68,7 +68,7 @@ func ApplyBaseTerraform(dryRun bool, directory string) { func DestroyBaseTerraform(skipBaseTerraform bool) { config := configs.ReadConfig() if !skipBaseTerraform { - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/base", config.HomePath) + directory := fmt.Sprintf("%s/gitops/terraform/base", config.K1srtFolderPath) err := os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 44feff0bd..533b1c2fb 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -104,7 +104,7 @@ func ConfigureVault(dryRun bool) { os.Setenv("TF_VAR_vault_token", viper.GetString("aws.hostedzonename")) os.Setenv("TF_VAR_vault_redirect_uris", "[\"will-be-patched-later\"]") - directory := fmt.Sprintf("%s/.kubefirst/gitops/terraform/vault", config.HomePath) + directory := fmt.Sprintf("%s/gitops/terraform/vault", config.K1srtFolderPath) err = os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) diff --git a/pkg/keys.go b/pkg/keys.go index b9d388acc..2422b712d 100644 --- a/pkg/keys.go +++ b/pkg/keys.go @@ -68,7 +68,7 @@ configs: %s `, strings.ReplaceAll(privateKey, "\n", "\n "))) - err := ioutil.WriteFile(fmt.Sprintf("%s/.kubefirst/argocd-init-values.yaml", config.HomePath), argocdInitValuesYaml, 0644) + err := ioutil.WriteFile(fmt.Sprintf("%s/argocd-init-values.yaml", config.K1srtFolderPath), argocdInitValuesYaml, 0644) if err != nil { log.Panicf("error: could not write argocd-init-values.yaml %s", err) } From f3c50d54a3b9d614e60a57fa1596f08c1f0ad19b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Wed, 13 Jul 2022 15:53:51 -0300 Subject: [PATCH 079/107] chore: update naming left overs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/clean.go | 8 ++++---- cmd/init.go | 4 ++-- cmd/root.go | 4 ++-- cmd/version.go | 2 +- configs/kubefirstFile.go | 6 +++--- internal/downloadManager/download.go | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/clean.go b/cmd/clean.go index 3e86f779f..72b779899 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -20,11 +20,11 @@ Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { - // todo delete the s3 buckets associated with the ~/.flare file + // todo delete the s3 buckets associated with the ~/.kubefirst file // todo ask for user input to verify deletion? config := configs.ReadConfig() - log.Println("removing $HOME/.kubefirst and $HOME/.flare") + log.Printf("removing %q and %q", config.KubeConfigPath, config.KubefirstConfigFilePath) // todo ask for user input to verify? err := os.RemoveAll(config.K1srtFolderPath) if err != nil { @@ -35,7 +35,7 @@ to quickly create a Cobra application.`, if err != nil { log.Panicf("unable to delete %q file, error is: ", err) } - log.Println("removed $HOME/.kubefirst and $HOME/.flare") + log.Printf("removed %q and %q", config.KubeConfigPath, config.KubefirstConfigFilePath) log.Printf("%q and %q folders were removed", config.K1srtFolderPath, config.KubectlClientPath) @@ -47,7 +47,7 @@ to quickly create a Cobra application.`, log.Panicf("error: could not create directory %q/tools - it must exist to continue %s", config.K1srtFolderPath, err) } - log.Println("created $HOME/.kubefirst and $HOME/.kubefirst/tools - proceed to `kubefirst init`") + log.Printf("created %q and %q/tools - proceed to `kubefirst init`", config.KubefirstConfigFilePath, config.K1srtFolderPath) }, } diff --git a/cmd/init.go b/cmd/init.go index 64f72d329..93d58262c 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -8,7 +8,7 @@ import ( "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/internal/aws" "github.com/kubefirst/kubefirst/internal/downloadManager" - "github.com/kubefirst/kubefirst/internal/telemetry" + "github.com/kubefirst/kubefirst/internal/telemetry" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -205,7 +205,7 @@ func init() { if err != nil { log.Panic(err) } - initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.flare") + initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.k1srt") log.SetPrefix("LOG: ") log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) diff --git a/cmd/root.go b/cmd/root.go index 39f146443..22209595e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -19,7 +19,7 @@ var rootCmd = &cobra.Command{ open source application delivery platform in under an hour. checkout the docs at docs.kubefirst.com.`, Run: func(cmd *cobra.Command, args []string) { - log.Println(viper.Get("name")) //! print value coming from ~/.flare --> ~/.kubefirst + log.Println(viper.Get("name")) }, } @@ -39,7 +39,7 @@ func init() { // Cobra supports persistent flags, which, if defined here, // will be global for your application. - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.flare)") + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kubefirst)") // Cobra also supports local flags, which will only run // when this action is called directly. diff --git a/cmd/version.go b/cmd/version.go index 3f0c34947..4613b512c 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -16,6 +16,6 @@ var versionCmd = &cobra.Command{ Long: `All software has versions. This is kubefirst's`, Run: func(cmd *cobra.Command, args []string) { config := configs.ReadConfig() - log.Printf("flare-cli golang utility version: v%s", config.KubefirstVersion) + log.Printf("kubefirst-cli golang utility version: v%s", config.KubefirstVersion) }, } diff --git a/configs/kubefirstFile.go b/configs/kubefirstFile.go index baec7261b..55870a47d 100755 --- a/configs/kubefirstFile.go +++ b/configs/kubefirstFile.go @@ -6,10 +6,10 @@ import ( "os" ) -// CheckKubefirstConfigFile validate if ~/.flare file is ready to be consumed. +// CheckKubefirstConfigFile validate if ~/.kubefirst file is ready to be consumed. func CheckKubefirstConfigFile(config *Config) error { - flareFile := fmt.Sprintf("%s", config.KubefirstConfigFilePath) - if _, err := os.Stat(flareFile); err != nil { + kubefirstFile := fmt.Sprintf("%s", config.KubefirstConfigFilePath) + if _, err := os.Stat(kubefirstFile); err != nil { errorMsg := fmt.Sprintf("unable to load %q file, error is: %s", config.KubefirstConfigFilePath, err) log.Println(errorMsg) return fmt.Errorf(errorMsg) diff --git a/internal/downloadManager/download.go b/internal/downloadManager/download.go index 64d0f337c..22775083c 100644 --- a/internal/downloadManager/download.go +++ b/internal/downloadManager/download.go @@ -127,7 +127,7 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke ) log.Printf("-> kubectl version:\n\t%s\n\t%s\n", helmStdOut, helmStdErr) - // currently argocd init values is generated by flare nebulous ssh + // currently argocd init values is generated by kubefirst ssh // todo helm install argocd --create-namespace --wait --values ~/.kubefirst/argocd-init-values.yaml argo/argo-cd if errHelm != nil { log.Panicf("error executing helm version command: %v", err) From 8555ff24726ab6357080a4c86b54d7c794f68563 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Wed, 13 Jul 2022 19:02:44 +0000 Subject: [PATCH 080/107] add new progress lib Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/progressPrinter/progress.go | 109 +++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 internal/progressPrinter/progress.go diff --git a/internal/progressPrinter/progress.go b/internal/progressPrinter/progress.go new file mode 100644 index 000000000..f3bfd534b --- /dev/null +++ b/internal/progressPrinter/progress.go @@ -0,0 +1,109 @@ +package progressPrinter + +import ( + "sync" + "github.com/jedib0t/go-pretty/v6/progress" + "flag" + "time" + "fmt" +) + +//Struct used to manage tracker object +// This object may evolve with more properties in th future +// when we have more fancier UI tools/styles. +type ActionTracker struct { + Tracker *progress.Tracker +} + + +// General state object +type progressPrinter struct { + Trackers map[string]*ActionTracker + pw progress.Writer +} + + +var instance *progressPrinter +var once sync.Once + +// Function used to initialize the component once in the execution. +// Usually called from the `cmd` `init` func or as early as possible on the execution. +// import ("github.com/kubefirst/nebulous/pkg") +// func init() { +// progressPrinter.GetInstance() +// progressPrinter.SetupProgress(5) // Number of bars for the entire run. +// } +func GetInstance() *progressPrinter { + once.Do(func() { + instance = &progressPrinter{} + instance.Trackers = make(map[string]*ActionTracker) + }) + return instance +} + + +// SetupProgress prepare the progress bar setting its initial configuration +// Used for general initialization of tracker object and overall counter +func SetupProgress(numTrackers int) { + flag.Parse() + fmt.Printf("Init actions: %d expected tasks ...\n\n", numTrackers) + // instantiate a Progress Writer and set up the options + instance.pw = progress.NewWriter() + instance.pw.SetAutoStop(false) + instance.pw.SetTrackerLength(30) + instance.pw.SetMessageWidth(29) + instance.pw.SetNumTrackersExpected(numTrackers) + instance.pw.SetSortBy(progress.SortByPercentDsc) + instance.pw.SetStyle(progress.StyleDefault) + instance.pw.SetTrackerPosition(progress.PositionRight) + instance.pw.SetUpdateFrequency(time.Millisecond * 100) + instance.pw.Style().Colors = progress.StyleColorsExample + instance.pw.Style().Options.PercentFormat = "%4.1f%%" + instance.pw.Style().Visibility.ETA = true + instance.pw.Style().Visibility.ETAOverall = true + instance.pw.Style().Visibility.Percentage = true + instance.pw.Style().Visibility.Time = true + instance.pw.Style().Visibility.TrackerOverall = true + instance.pw.Style().Visibility.Value = true + go instance.pw.Render() +} + +// Initialise a tracker object +// Prefer `AddTracker` to create trackers, due to simplicity. +func CreateTracker(title string, total int64) *progress.Tracker { + tracker := &progress.Tracker{ + Message: title, + Total: total, + Units: progress.UnitsDefault, + } + + instance.pw.AppendTracker(tracker) + return tracker +} + + +// Prints a log message near the current active tracker. +// Sample of usage: +// progressPrinter.LogMessage("- Waiting bootstrap") +func LogMessage(message string){ + instance.pw.Log(message) +} + +// Add Tracker (prefered way) +// Return a string for the key to be used on future uses +// Sample of usage: +// progressPrinter.AddTracker("step-base", "Apply Base ", 3) +// no need to instanciate, it is a singleton, only one instance already started before use. +func AddTracker(key string, title string, total int64) string { + instance.Trackers[key] = &ActionTracker{Tracker: CreateTracker(title, total)} + return key +} + + +// Increments a tracker based on the provided key +// if key is unkown it will error out. +// Sample of usage: +// progressPrinter.IncrementTracker("step-base", 1) +func IncrementTracker(key string, value int64) { + instance.Trackers[key].Tracker.Increment(int64(1)) +} From 980c46427445abebc267ba1f9b08cefd08cf834f Mon Sep 17 00:00:00 2001 From: John Dietz Date: Wed, 13 Jul 2022 17:58:17 -0400 Subject: [PATCH 081/107] create go-release-binaries workflow --- .github/workflows/go-release-binaries.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/go-release-binaries.yaml diff --git a/.github/workflows/go-release-binaries.yaml b/.github/workflows/go-release-binaries.yaml new file mode 100644 index 000000000..699732843 --- /dev/null +++ b/.github/workflows/go-release-binaries.yaml @@ -0,0 +1,15 @@ +on: + release: + types: [created] + +jobs: + release-linux-amd64: + name: release linux/amd64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: wangyoucao577/go-release-action@v1.29 + with: + github_token: ${{ secrets.RELEASE_TOKEN }} + goos: linux + goarch: amd64 From 5fe9b3cfb298626d9fb2bac2c5805d90523d4d25 Mon Sep 17 00:00:00 2001 From: John Dietz Date: Wed, 13 Jul 2022 18:27:42 -0400 Subject: [PATCH 082/107] add additional os/arch --- .github/workflows/go-release-binaries.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/go-release-binaries.yaml b/.github/workflows/go-release-binaries.yaml index 699732843..829254e7a 100644 --- a/.github/workflows/go-release-binaries.yaml +++ b/.github/workflows/go-release-binaries.yaml @@ -8,8 +8,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - uses: wangyoucao577/go-release-action@v1.29 + with: + github_token: ${{ secrets.RELEASE_TOKEN }} + goos: darwin + goarch: amd64 + - uses: wangyoucao577/go-release-action@v1.29 + with: + github_token: ${{ secrets.RELEASE_TOKEN }} + goos: darwin + goarch: arm64 - uses: wangyoucao577/go-release-action@v1.29 with: github_token: ${{ secrets.RELEASE_TOKEN }} goos: linux goarch: amd64 + - uses: wangyoucao577/go-release-action@v1.29 + with: + github_token: ${{ secrets.RELEASE_TOKEN }} + goos: linux + goarch: arm64 From 5e817ba900a07b24cd676ba332c7c7149bde2071 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 14 Jul 2022 11:55:35 +0000 Subject: [PATCH 083/107] Explore a smaller create flow Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 151 ++----------------------------------------- cmd/createUtils.go | 155 +++++++++++++++++++++++++++++++++++++++++++++ cmd/createv2.go | 40 ++++++++++++ 3 files changed, 202 insertions(+), 144 deletions(-) create mode 100644 cmd/createUtils.go create mode 100644 cmd/createv2.go diff --git a/cmd/create.go b/cmd/create.go index 151b02055..c7c711167 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -7,14 +7,11 @@ import ( "github.com/kubefirst/kubefirst/internal/gitlab" "github.com/kubefirst/kubefirst/internal/helm" "github.com/kubefirst/kubefirst/internal/softserve" - "github.com/kubefirst/kubefirst/internal/telemetry" "github.com/kubefirst/kubefirst/internal/terraform" "github.com/kubefirst/kubefirst/internal/vault" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" "log" "os" "os/exec" @@ -64,14 +61,9 @@ to quickly create a Cobra application.`, infoCmd.Run(cmd, args) - metricName := "kubefirst.mgmt_cluster_install.started" - metricDomain := viper.GetString("aws.hostedzonename") + + sendStartedInstallTelemetry(dryRun) - if !dryRun { - telemetry.SendTelemetry(metricDomain, metricName) - } else { - log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) - } directory := fmt.Sprintf("%s/gitops/terraform/base", config.K1srtFolderPath) terraform.ApplyBaseTerraform(dryRun, directory) @@ -102,35 +94,7 @@ to quickly create a Cobra application.`, Trackers[trackerStage22].Tracker.Increment(int64(1)) //! argocd was just helm installed - x := 50 - for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/argocd") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() - 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++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "pods", "-l", "app.kubernetes.io/name=argocd-server") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() - 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, continuing") - time.Sleep(15 * time.Second) - break - } - } + waitArgoCDToBeReady() kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") kPortForwardArgocd.Stdout = os.Stdout @@ -159,36 +123,7 @@ to quickly create a Cobra application.`, //! //* we need to stop here and wait for the vault namespace to exist and the vault pod to be ready //! - x = 50 - for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/vault") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() - 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 - } - } - x = 50 - for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "get", "pods", "-l", "vault-initialized=true") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() - 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 - } - } + waitVaultToBeInitialized() kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") kPortForwardVault.Stdout = os.Stdout kPortForwardVault.Stderr = os.Stderr @@ -197,37 +132,7 @@ to quickly create a Cobra application.`, if err != nil { log.Panicf("error: failed to port-forward to vault in main thread %s", err) } - - x = 50 - for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/gitlab") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() - if err != nil { - log.Println("Waiting gitlab namespace to be born") - time.Sleep(10 * time.Second) - } else { - log.Println("gitlab namespace found, continuing") - time.Sleep(5 * time.Second) - break - } - } - x = 50 - for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "get", "pods", "-l", "app=webservice") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() - if err != nil { - log.Println("Waiting gitlab pods to be born") - time.Sleep(10 * time.Second) - } else { - log.Println("gitlab pods found, continuing") - time.Sleep(15 * time.Second) - break - } - } + waitGitlabToBeReady() log.Println("waiting for gitlab") waitForGitlab(config) log.Println("gitlab is ready!") @@ -256,23 +161,7 @@ to quickly create a Cobra application.`, /** */ - - x = 50 - for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "pod", "-l", "vault-initialized=true", "-n", "vault") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() - if err != nil { - log.Println("Waiting vault to be born") - time.Sleep(10 * time.Second) - } else { - log.Println("a Pod was found, continuing") - time.Sleep(25 * time.Second) - break - } - } - + waitVaultToBeInitialized() waitForVaultUnseal(config) log.Println("vault unseal condition met - continuing") @@ -315,14 +204,7 @@ to quickly create a Cobra application.`, // todo kind: Application .repoURL: } } - - metricName = "kubefirst.mgmt_cluster_install.completed" - - if !dryRun { - telemetry.SendTelemetry(metricDomain, metricName) - } else { - log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) - } + sendCompleteInstallTelemetry(dryRun) time.Sleep(time.Millisecond * 100) }, } @@ -338,22 +220,3 @@ func init() { } -// todo: move it to internals/ArgoCD -func setArgocdCreds() { - cfg := configs.ReadConfig() - config, err := clientcmd.BuildConfigFromFlags("", cfg.KubeConfigPath) - if err != nil { - panic(err.Error()) - } - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - panic(err.Error()) - } - argocdSecretClient = clientset.CoreV1().Secrets("argocd") - - argocdPassword := getSecretValue(argocdSecretClient, "argocd-initial-admin-secret", "password") - - viper.Set("argocd.admin.password", argocdPassword) - viper.Set("argocd.admin.username", "admin") - viper.WriteConfig() -} diff --git a/cmd/createUtils.go b/cmd/createUtils.go new file mode 100644 index 000000000..06e77be58 --- /dev/null +++ b/cmd/createUtils.go @@ -0,0 +1,155 @@ +package cmd + +import ( + "github.com/kubefirst/kubefirst/internal/telemetry" + "github.com/spf13/viper" + "github.com/kubefirst/kubefirst/configs" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/kubernetes" + "log" + "os" + "os/exec" + "time" +) + + + +// todo: move it to internals/ArgoCD +func setArgocdCreds() { + cfg := configs.ReadConfig() + config, err := clientcmd.BuildConfigFromFlags("", cfg.KubeConfigPath) + if err != nil { + panic(err.Error()) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) + } + argocdSecretClient = clientset.CoreV1().Secrets("argocd") + + argocdPassword := getSecretValue(argocdSecretClient, "argocd-initial-admin-secret", "password") + + viper.Set("argocd.admin.password", argocdPassword) + viper.Set("argocd.admin.username", "admin") + viper.WriteConfig() +} + +func sendStartedInstallTelemetry(dryRun bool){ + metricName := "kubefirst.mgmt_cluster_install.started" + if !dryRun { + telemetry.SendTelemetry( viper.GetString("aws.hostedzonename"), metricName) + } else { + log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) + } +} + +func sendCompleteInstallTelemetry(dryRun bool){ + metricName := "kubefirst.mgmt_cluster_install.completed" + if !dryRun { + telemetry.SendTelemetry(viper.GetString("aws.hostedzonename"), metricName) + } else { + log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) + } +} + +func waitArgoCDToBeReady(){ + config := configs.ReadConfig() + x := 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/argocd") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + 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++ { + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "pods", "-l", "app.kubernetes.io/name=argocd-server") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + 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, continuing") + time.Sleep(15 * time.Second) + break + } + } +} + +func waitVaultToBeInitialized() { + config := configs.ReadConfig() + x := 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/vault") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + 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 + } + } + x = 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "get", "pods", "-l", "vault-initialized=true") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + 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 + } + } +} + +func waitGitlabToBeReady() { + config := configs.ReadConfig() + x := 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/gitlab") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + if err != nil { + log.Println("Waiting gitlab namespace to be born") + time.Sleep(10 * time.Second) + } else { + log.Println("gitlab namespace found, continuing") + time.Sleep(5 * time.Second) + break + } + } + x = 50 + for i := 0; i < x; i++ { + kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "get", "pods", "-l", "app=webservice") + kGetNamespace.Stdout = os.Stdout + kGetNamespace.Stderr = os.Stderr + err := kGetNamespace.Run() + if err != nil { + log.Println("Waiting gitlab pods to be born") + time.Sleep(10 * time.Second) + } else { + log.Println("gitlab pods found, continuing") + time.Sleep(15 * time.Second) + break + } + } + +} diff --git a/cmd/createv2.go b/cmd/createv2.go new file mode 100644 index 000000000..6e8dea14d --- /dev/null +++ b/cmd/createv2.go @@ -0,0 +1,40 @@ +/* +Copyright © 2022 NAME HERE + +*/ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// createv2Cmd represents the createv2 command +var createv2Cmd = &cobra.Command{ + Use: "createv2", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("createv2 called") + }, +} + +func init() { + rootCmd.AddCommand(createv2Cmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // createv2Cmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // createv2Cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} From 78c99afcf3ce3a66644483781468d4c6988ac5e6 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 14 Jul 2022 13:54:30 +0000 Subject: [PATCH 084/107] restore dry-run create Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 209 ++++++++++++++++----------- cmd/createUtils.go | 36 ++++- cmd/createv2.go | 40 ----- cmd/kubernetes.go | 24 ++- internal/gitlab/gitlab.go | 7 +- internal/progressPrinter/progress.go | 2 + internal/softserve/softserve.go | 2 +- 7 files changed, 187 insertions(+), 133 deletions(-) delete mode 100644 cmd/createv2.go diff --git a/cmd/create.go b/cmd/create.go index c7c711167..a5048a008 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -9,7 +9,6 @@ import ( "github.com/kubefirst/kubefirst/internal/softserve" "github.com/kubefirst/kubefirst/internal/terraform" "github.com/kubefirst/kubefirst/internal/vault" - "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" "log" @@ -17,6 +16,7 @@ import ( "os/exec" "syscall" "time" + "github.com/kubefirst/kubefirst/internal/progressPrinter" ) const trackerStage20 = "0 - Apply Base" @@ -36,6 +36,7 @@ This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { + progressPrinter.AddTracker("step-0", "Process Parameters", 1) config := configs.ReadConfig() skipVault, err := cmd.Flags().GetBool("skip-vault") @@ -51,69 +52,84 @@ to quickly create a Cobra application.`, log.Panic(err) } - pkg.SetupProgress(4) - Trackers := make(map[string]*pkg.ActionTracker) - - Trackers[trackerStage20] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage20, 1)} - Trackers[trackerStage21] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage21, 2)} - Trackers[trackerStage22] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage22, 7)} - Trackers[trackerStage23] = &pkg.ActionTracker{Tracker: pkg.CreateTracker(trackerStage23, 3)} - infoCmd.Run(cmd, args) + progressPrinter.IncrementTracker("step-0", 1) + - + progressPrinter.AddTracker("step-softserve", "Prepare Temporary Repo ", 4) sendStartedInstallTelemetry(dryRun) + progressPrinter.IncrementTracker("step-softserve", 1) directory := fmt.Sprintf("%s/gitops/terraform/base", config.K1srtFolderPath) + informUser("Creating K8S Cluster") terraform.ApplyBaseTerraform(dryRun, directory) - Trackers[trackerStage20].Tracker.Increment(int64(1)) + progressPrinter.IncrementTracker("step-softserve", 1) //! soft-serve was just applied softserve.CreateSoftServe(dryRun, config.KubeConfigPath) - waitForNamespaceandPods(config, "soft-serve", "app=soft-serve") + informUser("Created Softserve") + progressPrinter.IncrementTracker("step-softserve", 1) + informUser("Waiting Softserve") + waitForNamespaceandPods(dryRun, config, "soft-serve", "app=soft-serve") + progressPrinter.IncrementTracker("step-softserve", 1) // todo this should be replaced with something more intelligent - log.Println("waiting for soft-serve installation to complete...") - time.Sleep(60 * time.Second) - - kPortForwardSoftServe := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") - kPortForwardSoftServe.Stdout = os.Stdout - kPortForwardSoftServe.Stderr = os.Stderr - err = kPortForwardSoftServe.Start() - defer kPortForwardSoftServe.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to soft-serve %s", err) + log.Println("Waiting for soft-serve installation to complete...") + if !dryRun { + time.Sleep(60 * time.Second) + kPortForwardSoftServe := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") + kPortForwardSoftServe.Stdout = os.Stdout + kPortForwardSoftServe.Stderr = os.Stderr + err = kPortForwardSoftServe.Start() + defer kPortForwardSoftServe.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to soft-serve %s", err) + } + time.Sleep(20 * time.Second) } - time.Sleep(20 * time.Second) - Trackers[trackerStage21].Tracker.Increment(int64(1)) - softserve.ConfigureSoftServeAndPush(dryRun) - Trackers[trackerStage21].Tracker.Increment(int64(1)) + informUser("Softserve Update") + softserve.ConfigureSoftServeAndPush(dryRun) + progressPrinter.IncrementTracker("step-softserve", 1) + + progressPrinter.AddTracker("step-argo", "Deploy CI/CD ", 5) + informUser("Deploy ArgoCD") + progressPrinter.IncrementTracker("step-argo", 1) helm.InstallArgocd(dryRun) - Trackers[trackerStage22].Tracker.Increment(int64(1)) //! argocd was just helm installed - waitArgoCDToBeReady() + waitArgoCDToBeReady(dryRun) + informUser("ArgoCD Ready") + progressPrinter.IncrementTracker("step-argo", 1) + if !dryRun { + kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") + kPortForwardArgocd.Stdout = os.Stdout + kPortForwardArgocd.Stderr = os.Stderr + err = kPortForwardArgocd.Start() + defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to argocd in main thread %s", err) + } - kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") - kPortForwardArgocd.Stdout = os.Stdout - kPortForwardArgocd.Stderr = os.Stderr - err = kPortForwardArgocd.Start() - defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to argocd in main thread %s", err) + log.Println("sleeping for 45 seconds, hurry up jared") + time.Sleep(45 * time.Second) } + informUser(fmt.Sprintf("ArgoCD available at %s", viper.GetString("argocd.local.service"))) + progressPrinter.IncrementTracker("step-argo", 1) - log.Println("sleeping for 45 seconds, hurry up jared") - time.Sleep(45 * time.Second) + informUser("Setting argocd credentials") + setArgocdCreds(dryRun) + progressPrinter.IncrementTracker("step-argo", 1) - log.Println("setting argocd credentials") - setArgocdCreds() - log.Println("getting an argocd auth token") + informUser("Getting an argocd auth token") token := argocd.GetArgocdAuthToken(dryRun) - log.Println("syncing the registry application") + progressPrinter.IncrementTracker("step-argo", 1) + + informUser("Syncing the registry application") argocd.SyncArgocdApplication(dryRun, "registry", token) + progressPrinter.IncrementTracker("step-argo", 1) + // todo, need to stall until the registry has synced, then get to ui asap //! skip this if syncing from argocd and not helm installing @@ -123,82 +139,109 @@ to quickly create a Cobra application.`, //! //* we need to stop here and wait for the vault namespace to exist and the vault pod to be ready //! - waitVaultToBeInitialized() - kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") - kPortForwardVault.Stdout = os.Stdout - kPortForwardVault.Stderr = os.Stderr - err = kPortForwardVault.Start() - defer kPortForwardVault.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to vault in main thread %s", err) + progressPrinter.AddTracker("step-gitlab", "Setup Gitlab", 6) + informUser("Waiting vault to be ready") + waitVaultToBeInitialized(dryRun) + progressPrinter.IncrementTracker("step-gitlab", 1) + if !dryRun { + kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + kPortForwardVault.Stdout = os.Stdout + kPortForwardVault.Stderr = os.Stderr + err = kPortForwardVault.Start() + defer kPortForwardVault.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to vault in main thread %s", err) + } } - waitGitlabToBeReady() + informUser(fmt.Sprintf("Vault available at %s", viper.GetString("vault.local.service"))) + progressPrinter.IncrementTracker("step-gitlab", 1) + + informUser("Waiting gitlab to be ready") + waitGitlabToBeReady(dryRun) log.Println("waiting for gitlab") - waitForGitlab(config) + waitForGitlab(dryRun, config) log.Println("gitlab is ready!") - kPortForwardGitlab := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") - kPortForwardGitlab.Stdout = os.Stdout - kPortForwardGitlab.Stderr = os.Stderr - err = kPortForwardGitlab.Start() - defer kPortForwardGitlab.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) + progressPrinter.IncrementTracker("step-gitlab", 1) + + if !dryRun { + kPortForwardGitlab := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + kPortForwardGitlab.Stdout = os.Stdout + kPortForwardGitlab.Stderr = os.Stderr + err = kPortForwardGitlab.Start() + defer kPortForwardGitlab.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) + } } + informUser(fmt.Sprintf("Gitlab available at %s", viper.GetString("gitlab.local.service"))) + progressPrinter.IncrementTracker("step-gitlab", 1) if !skipGitlab { // TODO: Confirm if we need to waitgit lab to be ready // OR something, too fast the secret will not be there. + informUser("Gitlab setup tokens") gitlab.ProduceGitlabTokens(dryRun) - Trackers[trackerStage22].Tracker.Increment(int64(1)) + progressPrinter.IncrementTracker("step-gitlab", 1) + informUser("Gitlab terraform") gitlab.ApplyGitlabTerraform(dryRun, directory) - Trackers[trackerStage22].Tracker.Increment(int64(1)) gitlab.GitlabKeyUpload(dryRun) - Trackers[trackerStage22].Tracker.Increment(int64(1)) + informUser("Gitlab ready") + progressPrinter.IncrementTracker("step-gitlab", 1) if !skipVault { - log.Println("waiting for vault unseal") + progressPrinter.AddTracker("step-vault", "Configure Vault", 4) + informUser("waiting for vault unseal") /** */ - waitVaultToBeInitialized() - waitForVaultUnseal(config) - log.Println("vault unseal condition met - continuing") + waitVaultToBeInitialized(dryRun) + informUser("Vault initialized") + progressPrinter.IncrementTracker("step-vault", 1) + + waitForVaultUnseal(dryRun,config) + informUser("Vault unseal") + progressPrinter.IncrementTracker("step-vault", 1) log.Println("configuring vault") vault.ConfigureVault(dryRun) - log.Println("vault configured") + informUser("Vault configured") + progressPrinter.IncrementTracker("step-vault", 1) log.Println("creating vault configured secret") - createVaultConfiguredSecret(config) - log.Println("vault-configured secret created") + createVaultConfiguredSecret(dryRun, config) + informUser("Vault secret created") + progressPrinter.IncrementTracker("step-vault", 1) - Trackers[trackerStage23].Tracker.Increment(int64(1)) + + progressPrinter.AddTracker("step-post-gitlab", "Finalize Gitlab updates", 5) vault.AddGitlabOidcApplications(dryRun) - Trackers[trackerStage23].Tracker.Increment(int64(1)) + informUser("Added Gitlab OIDC") + progressPrinter.IncrementTracker("step-post-gitlab", 1) + - log.Println("waiting for gitlab dns to propagate before continuing") + informUser("Waiting for Gitlab dns to propagate before continuing") gitlab.AwaitGitlab(dryRun) - Trackers[trackerStage22].Tracker.Increment(int64(1)) + progressPrinter.IncrementTracker("step-post-gitlab", 1) + - log.Println("pushing gitops repo to origin gitlab") + informUser("Pushing gitops repo to origin gitlab") // refactor: sounds like a new functions, should PushGitOpsToGitLab be renamed/update signature? - gitlab.PushGitRepo(config, "gitlab", "gitops") // todo: need to handle if this was already pushed, errors on failure) + gitlab.PushGitRepo(dryRun, config, "gitlab", "gitops") // todo: need to handle if this was already pushed, errors on failure) + progressPrinter.IncrementTracker("step-post-gitlab", 1) // todo: keep one of the two git push functions, they're similar, but not exactly the same //gitlab.PushGitOpsToGitLab(dryRun) - log.Println("pushing metaphor repo to origin gitlab") - gitlab.PushGitRepo(config, "gitlab", "metaphor") + informUser("Pushing metaphor repo to origin gitlab") + gitlab.PushGitRepo(dryRun, config, "gitlab", "metaphor") + progressPrinter.IncrementTracker("step-post-gitlab", 1) // todo: keep one of the two git push functions, they're similar, but not exactly the same //gitlab.PushGitOpsToGitLab(dryRun) - Trackers[trackerStage23].Tracker.Increment(int64(1)) - Trackers[trackerStage22].Tracker.Increment(int64(1)) + informUser("Changing registry to Gitlab") gitlab.ChangeRegistryToGitLab(dryRun) - Trackers[trackerStage22].Tracker.Increment(int64(1)) - - Trackers[trackerStage23].Tracker.Increment(int64(1)) + progressPrinter.IncrementTracker("step-post-gitlab", 1) // todo triage / force apply the contents adjusting // todo kind: Application .repoURL: @@ -217,6 +260,8 @@ func init() { createCmd.Flags().Bool("dry-run", false, "set to dry-run mode, no changes done on cloud provider selected") createCmd.Flags().Bool("skip-gitlab", false, "Skip GitLab lab install and vault setup") createCmd.Flags().Bool("skip-vault", false, "Skip post-gitClient lab install and vault setup") - + + progressPrinter.GetInstance() + progressPrinter.SetupProgress(4) } diff --git a/cmd/createUtils.go b/cmd/createUtils.go index 06e77be58..06f96a69d 100644 --- a/cmd/createUtils.go +++ b/cmd/createUtils.go @@ -6,16 +6,26 @@ import ( "github.com/kubefirst/kubefirst/configs" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/kubernetes" + "github.com/kubefirst/kubefirst/internal/progressPrinter" "log" "os" "os/exec" "time" + "fmt" ) // todo: move it to internals/ArgoCD -func setArgocdCreds() { +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 + } + cfg := configs.ReadConfig() config, err := clientcmd.BuildConfigFromFlags("", cfg.KubeConfigPath) if err != nil { @@ -52,7 +62,11 @@ func sendCompleteInstallTelemetry(dryRun bool){ } } -func 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++ { @@ -85,7 +99,11 @@ func waitArgoCDToBeReady(){ } } -func waitVaultToBeInitialized() { +func waitVaultToBeInitialized(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, waitVaultToBeInitialized skipped.") + return + } config := configs.ReadConfig() x := 50 for i := 0; i < x; i++ { @@ -119,7 +137,11 @@ func waitVaultToBeInitialized() { } } -func waitGitlabToBeReady() { +func waitGitlabToBeReady(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, waitVaultToBeInitialized skipped.") + return + } config := configs.ReadConfig() x := 50 for i := 0; i < x; i++ { @@ -153,3 +175,9 @@ func waitGitlabToBeReady() { } } + +//Notify user in the STOUT and also logfile +func informUser(message string){ + log.Println(message) + progressPrinter.LogMessage(fmt.Sprintf("- %s",message)) +} diff --git a/cmd/createv2.go b/cmd/createv2.go deleted file mode 100644 index 6e8dea14d..000000000 --- a/cmd/createv2.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright © 2022 NAME HERE - -*/ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -// createv2Cmd represents the createv2 command -var createv2Cmd = &cobra.Command{ - Use: "createv2", - Short: "A brief description of your command", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("createv2 called") - }, -} - -func init() { - rootCmd.AddCommand(createv2Cmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // createv2Cmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // createv2Cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") -} diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index 9da7c77b1..95eac3d9d 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -40,7 +40,11 @@ func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) return gitlabToolboxPodName } -func waitForVaultUnseal(config *configs.Config) { +func waitForVaultUnseal(dryRun bool, config *configs.Config) { + if dryRun { + log.Printf("[#99] Dry-run mode, waitForGitlab skipped.") + return + } vaultReady := viper.GetBool("create.vault.ready") if !vaultReady { var output bytes.Buffer @@ -60,7 +64,11 @@ func waitForVaultUnseal(config *configs.Config) { } -func waitForGitlab(config *configs.Config) { +func waitForGitlab(dryRun bool, config *configs.Config) { + if dryRun { + log.Printf("[#99] Dry-run mode, waitForGitlab skipped.") + return + } var output bytes.Buffer // todo - add a viper.GetBool() check to the beginning of this function // todo write in golang? see here -> https://github.com/bcreane/k8sutils/blob/master/utils.go @@ -74,7 +82,11 @@ func waitForGitlab(config *configs.Config) { log.Printf("the output is: %s", output.String()) } -func createVaultConfiguredSecret(config *configs.Config) { +func createVaultConfiguredSecret(dryRun bool, config *configs.Config) { + if dryRun { + log.Printf("[#99] Dry-run mode, createVaultConfiguredSecret skipped.") + return + } if !viper.GetBool("vault.configuredsecret") { var output bytes.Buffer // todo - https://github.com/bcreane/k8sutils/blob/master/utils.go @@ -124,7 +136,11 @@ func getSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key strin return string(secret.Data[key]) } -func waitForNamespaceandPods(config *configs.Config, namespace, podLabel string) { +func waitForNamespaceandPods(dryRun bool, config *configs.Config, namespace, podLabel string) { + if dryRun { + log.Printf("[#99] Dry-run mode, waitForNamespaceandPods skipped") + return + } if !viper.GetBool("create.softserve.ready") { x := 50 for i := 0; i < x; i++ { diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 8f19e1f0a..c1468290d 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -532,8 +532,11 @@ func HydrateGitlabMetaphorRepo(dryRun bool) { } // refactor: review it -func PushGitRepo(config *configs.Config, gitOrigin, repoName string) { - +func PushGitRepo(dryRun bool, config *configs.Config, gitOrigin, repoName string) { + if dryRun { + log.Printf("[#99] Dry-run mode, PushGitRepo skipped.") + return + } repoDir := fmt.Sprintf("%s/%s", config.K1srtFolderPath, repoName) repo, err := git.PlainOpen(repoDir) if err != nil { diff --git a/internal/progressPrinter/progress.go b/internal/progressPrinter/progress.go index f3bfd534b..1fb1d6ac3 100644 --- a/internal/progressPrinter/progress.go +++ b/internal/progressPrinter/progress.go @@ -95,6 +95,7 @@ func LogMessage(message string){ // progressPrinter.AddTracker("step-base", "Apply Base ", 3) // no need to instanciate, it is a singleton, only one instance already started before use. func AddTracker(key string, title string, total int64) string { + time.Sleep(1 * time.Second) instance.Trackers[key] = &ActionTracker{Tracker: CreateTracker(title, total)} return key } @@ -105,5 +106,6 @@ func AddTracker(key string, title string, total int64) string { // Sample of usage: // progressPrinter.IncrementTracker("step-base", 1) func IncrementTracker(key string, value int64) { + time.Sleep(1 * time.Second) instance.Trackers[key].Tracker.Increment(int64(1)) } diff --git a/internal/softserve/softserve.go b/internal/softserve/softserve.go index afd85672f..92d645a36 100644 --- a/internal/softserve/softserve.go +++ b/internal/softserve/softserve.go @@ -54,7 +54,7 @@ func ConfigureSoftServeAndPush(dryRun bool) { configureSoftServe() // refactor: update it - gitlab.PushGitRepo(config, "soft", "gitops") + gitlab.PushGitRepo(dryRun, config, "soft", "gitops") viper.Set("create.softserve.configure", true) viper.WriteConfig() From 69ce039eddb0a211b8d0bc3f1708bd821edc58ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 14 Jul 2022 12:06:10 -0300 Subject: [PATCH 085/107] feat: implement handoff screen for clean command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/clean.go | 75 +++++++++++++++------ cmd/root.go | 41 +----------- go.mod | 9 +++ go.sum | 30 +++++++++ internal/reports/report.go | 133 +++++++++++++++++++++++++++++++++++++ main.go | 52 +++++++++++++-- pkg/helpers.go | 30 +++++++++ 7 files changed, 303 insertions(+), 67 deletions(-) create mode 100644 internal/reports/report.go diff --git a/cmd/clean.go b/cmd/clean.go index 72b779899..867b12a78 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -1,32 +1,38 @@ package cmd import ( + "bytes" "fmt" "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/reports" + "github.com/spf13/cobra" + "github.com/spf13/viper" "log" "os" - - "github.com/spf13/cobra" + "strings" ) -// cleanCmd represents the clean command +// todo delete the s3 buckets associated with the ~/.kubefirst file +// todo ask for user input to verify deletion? +// todo ask for user input to verify? +// cleanCmd removes all kubefirst resources locally for new execution. var cleanCmd = &cobra.Command{ Use: "clean", Short: "removes all kubefirst resources locally for new execution", - Long: `A longer description that spans multiple lines and likely contains examples -and usage of using your command. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, + Long: `Kubefirst creates files and folders during installation at your local environment. This command removes all +Kubefirst files.`, Run: func(cmd *cobra.Command, args []string) { - // todo delete the s3 buckets associated with the ~/.kubefirst file - // todo ask for user input to verify deletion? + config := configs.ReadConfig() - log.Printf("removing %q and %q", config.KubeConfigPath, config.KubefirstConfigFilePath) - // todo ask for user input to verify? - err := os.RemoveAll(config.K1srtFolderPath) + // command line flags + rmLogsFolder, err := cmd.Flags().GetBool("rm-logs") + if err != nil { + log.Panic(err) + } + + // delete files and folders + err = os.RemoveAll(config.K1srtFolderPath) if err != nil { log.Panicf("unable to delete %q folder, error is: %s", config.K1srtFolderPath, err) } @@ -35,22 +41,49 @@ to quickly create a Cobra application.`, if err != nil { log.Panicf("unable to delete %q file, error is: ", err) } - log.Printf("removed %q and %q", config.KubeConfigPath, config.KubefirstConfigFilePath) - log.Printf("%q and %q folders were removed", config.K1srtFolderPath, config.KubectlClientPath) + // remove logs folder if flag is enabled + var logFolderLocation string + if rmLogsFolder { + logFolderLocation = viper.GetString("log-folder-location") + err := os.RemoveAll(logFolderLocation) + if err != nil { + log.Panicf("unable to delete logs folder at %q", config.KubefirstLogPath) + } + } + // re-create folder if err := os.Mkdir(fmt.Sprintf("%s", config.K1srtFolderPath), os.ModePerm); err != nil { log.Panicf("error: could not create directory %q - it must exist to continue. error is: %s", config.K1srtFolderPath, err) } - toolsDir := fmt.Sprintf("%s/tools", config.K1srtFolderPath) - if err := os.Mkdir(toolsDir, os.ModePerm); err != nil { - log.Panicf("error: could not create directory %q/tools - it must exist to continue %s", config.K1srtFolderPath, err) + + // re-create base + log.Printf("%q config file and %q folder were deleted and re-created", config.KubefirstConfigFilePath, config.K1srtFolderPath) + + var cleanSummary bytes.Buffer + cleanSummary.WriteString(strings.Repeat("-", 70)) + cleanSummary.WriteString("\nclean summary:\n") + cleanSummary.WriteString(strings.Repeat("-", 70)) + cleanSummary.WriteString("\n\nFiles and folders deleted:\n\n") + + cleanSummary.WriteString(fmt.Sprintf(" %q\n", config.KubefirstConfigFilePath)) + cleanSummary.WriteString(fmt.Sprintf(" %q\n", config.K1srtFolderPath)) + + if rmLogsFolder { + cleanSummary.WriteString(fmt.Sprintf(" %q\n", logFolderLocation)) } - log.Printf("created %q and %q/tools - proceed to `kubefirst init`", config.KubefirstConfigFilePath, config.K1srtFolderPath) + cleanSummary.WriteString("\nRe-created empty folder: \n\n") + cleanSummary.WriteString(fmt.Sprintf(" %q\n\n", config.K1srtFolderPath)) + + cleanSummary.WriteString("Re-created empty config file: \n\n") + cleanSummary.WriteString(fmt.Sprintf(" %q", config.KubefirstConfigFilePath)) + + reports.CleanSummary(cleanSummary) }, } func init() { - initCmd.AddCommand(cleanCmd) + rootCmd.AddCommand(cleanCmd) + cleanCmd.Flags().Bool("rm-logs", false, "remove logs folder") } diff --git a/cmd/root.go b/cmd/root.go index 22209595e..584e2451c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,16 +1,12 @@ package cmd import ( - "errors" - "github.com/kubefirst/kubefirst/configs" "github.com/spf13/cobra" "github.com/spf13/viper" "log" "os" ) -var cfgFile string - // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "kubefirst", @@ -33,41 +29,8 @@ func Execute() { } func init() { - cobra.OnInitialize(initConfig) - - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application. - - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kubefirst)") + cobra.OnInitialize() - // Cobra also supports local flags, which will only run - // when this action is called directly. + // 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") } - -// initConfig reads in config file and ENV variables if set. -func initConfig() { - config := configs.ReadConfig() - if cfgFile == "" { - cfgFile = config.KubefirstConfigFilePath - } - - if _, err := os.Stat(cfgFile); errors.Is(err, os.ErrNotExist) { - log.Printf("Config file not found, creating a blank one: %s \n", cfgFile) - err := os.WriteFile(cfgFile, []byte("createdBy: installer\n\n"), 0700) - if err != nil { - log.Panic(err) - } - - } - viper.SetConfigFile(cfgFile) - viper.SetConfigType("yaml") - viper.AutomaticEnv() // read in environment variables that match - - // If a config file is found, read it in. - if err := viper.ReadInConfig(); err == nil { - log.Println("Using config file:", viper.ConfigFileUsed()) - } - -} diff --git a/go.mod b/go.mod index 78ec3e2d8..cbd102a08 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/route53 v1.20.5 github.com/aws/aws-sdk-go-v2/service/sts v1.16.6 github.com/caarlos0/env/v6 v6.9.3 + github.com/charmbracelet/bubbles v0.13.0 + github.com/charmbracelet/bubbletea v0.22.0 + github.com/charmbracelet/lipgloss v0.5.0 github.com/cip8/autoname v1.0.0 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-git/v5 v5.4.2 @@ -43,6 +46,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.11.5 // indirect github.com/aws/smithy-go v1.11.3 // indirect github.com/cenkalti/backoff/v3 v3.0.0 // indirect + github.com/containerd/console v1.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/fatih/color v1.13.0 // indirect @@ -81,6 +85,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect @@ -92,6 +97,10 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/cancelreader v0.2.1 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect github.com/oklog/run v1.0.0 // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect diff --git a/go.sum b/go.sum index 6e5e0ba49..25bc7552d 100644 --- a/go.sum +++ b/go.sum @@ -92,6 +92,7 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go v1.44.23 h1:oFvpKJk5qdprnCcuCWk2/CADdvfYtyduQ392bMXjlYI= github.com/aws/aws-sdk-go v1.44.23/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v1.16.4/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= @@ -133,6 +134,14 @@ github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4r github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.13.0 h1:zP/ROH3wJEBqZWKIsD50ZKKlx3ydLInq3LdD/Nrlb8w= +github.com/charmbracelet/bubbles v0.13.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= +github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= +github.com/charmbracelet/bubbletea v0.22.0 h1:E1BTNSE3iIrq0G0X6TjGAmrQ32cGCbFDPcIuImikrUc= +github.com/charmbracelet/bubbletea v0.22.0/go.mod h1:aoVIwlNlr5wbCB26KhxfrqAn0bMp4YpJcoOelbxApjs= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= +github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -149,6 +158,8 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -435,6 +446,9 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -454,6 +468,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -489,6 +505,17 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/cancelreader v0.2.1 h1:Xzd1B4U5bWQOuSKuN398MyynIGTNT89dxzpEDsalXZs= +github.com/muesli/cancelreader v0.2.1/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= @@ -536,6 +563,7 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -546,6 +574,7 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sagikazarmark/crypt v0.5.0/go.mod h1:l+nzl7KWh51rpzp2h7t4MZWyiEWdhNpOAnclKvg+mdA= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -823,6 +852,7 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/reports/report.go b/internal/reports/report.go new file mode 100644 index 000000000..2496a6ad7 --- /dev/null +++ b/internal/reports/report.go @@ -0,0 +1,133 @@ +package reports + +import ( + "bytes" + "fmt" + "log" + "strings" + + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +var ( + titleStyle = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Right = "├" + return lipgloss.NewStyle().BorderStyle(b).Padding(0, 1) + }() + + infoStyle = func() lipgloss.Style { + b := lipgloss.RoundedBorder() + b.Left = "┤" + return titleStyle.Copy().BorderStyle(b) + }() +) + +type Model struct { + Content string + ready bool + viewport viewport.Model +} + +func (m Model) Init() tea.Cmd { + return nil +} + +func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var ( + cmd tea.Cmd + cmds []tea.Cmd + ) + + // allow key mapping for exit screen + switch msg := msg.(type) { + case tea.KeyMsg: + if k := msg.String(); k == "ctrl+c" || k == "q" || k == "esc" { + return m, tea.Quit + } + + case tea.WindowSizeMsg: + headerHeight := lipgloss.Height(m.headerView()) + footerHeight := lipgloss.Height(m.footerView()) + verticalMarginHeight := headerHeight + footerHeight + + if !m.ready { + // Since this program is using the full size of the viewport we + // need to wait until we've received the window dimensions before + // we can initialize the viewport. The initial dimensions come in + // quickly, though asynchronously, which is why we wait for them + // here. + m.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight) + m.viewport.YPosition = headerHeight + m.viewport.SetContent(m.Content) + m.ready = true + + // This is only necessary for high performance rendering, which in + // most cases you won't need. + // + // Render the viewport one line below the header. + m.viewport.YPosition = headerHeight + 1 + } else { + m.viewport.Width = msg.Width + m.viewport.Height = msg.Height - verticalMarginHeight + } + } + + // Handle keyboard and mouse events in the viewport + m.viewport, cmd = m.viewport.Update(msg) + cmds = append(cmds, cmd) + + return m, tea.Batch(cmds...) +} + +func (m Model) View() string { + if !m.ready { + return "\n Initializing..." + } + return fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.viewport.View(), m.footerView()) +} + +func (m Model) headerView() string { + title := titleStyle.Render("Kubefirst cluster management") + line := strings.Repeat("─", max(0, m.viewport.Width-lipgloss.Width(title))) + return lipgloss.JoinHorizontal(lipgloss.Center, title, line) +} + +func (m Model) footerView() string { + info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) + line := strings.Repeat("─", max(0, m.viewport.Width-lipgloss.Width(info))) + return lipgloss.JoinHorizontal(lipgloss.Center, line, info) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// CleanSummary receives a well-formatted buffer of bytes, and style it to the output. +func CleanSummary(cleanSummary bytes.Buffer) { + + const kubefirstBoldPurple = "#d0bae9" + const kubefirstLightPurple = "#3c356c" + + var style = lipgloss.NewStyle(). + Foreground(lipgloss.Color(kubefirstBoldPurple)). + Background(lipgloss.Color(kubefirstLightPurple)). + PaddingTop(2). + PaddingBottom(2). + PaddingLeft(2). + PaddingRight(2). + Width(75) + + p := tea.NewProgram( + Model{Content: style.Render(cleanSummary.String())}, + ) + + if err := p.Start(); err != nil { + log.Panicf("unable to load reports screen, error is: %s", err) + } +} diff --git a/main.go b/main.go index 182adac79..1bdd32e7a 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,9 @@ package main import ( "fmt" "github.com/kubefirst/kubefirst/cmd" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/pkg" + "github.com/spf13/viper" "log" "os" "time" @@ -15,16 +18,51 @@ import ( func main() { now := time.Now() epoch := now.Unix() - logsdir := "log" - os.Mkdir(logsdir, 0700) - logfile := fmt.Sprintf("./%s/log_%d.log", logsdir, epoch) - fmt.Printf("Result will be logged at: %s \n", logfile) + + currentFolder, err := os.Getwd() + if err != nil { + log.Panicf("unable to get current folder location, error is: %s", err) + } + logsFolder := fmt.Sprintf("%s/%s", currentFolder, "logs") + // we're ignoring folder creation handling at the moment + // todo: add folder creation handler + _ = os.Mkdir(logsFolder, 0700) + + logfile := fmt.Sprintf("%s/log_%d.log", logsFolder, epoch) + fmt.Printf("Logging at: %s \n", logfile) + + config := configs.ReadConfig() + + err = pkg.SetupViper(config) + if err != nil { + log.Panic(err) + } + + viper.Set("log-folder-location", logsFolder) + err = viper.WriteConfig() + if err != nil { + log.Panicf("unable to set log-file-location, error is: %s", err) + } + file, err := openLogFile(logfile) - defer file.Close() - log.SetOutput(file) if err != nil { - log.Fatal(err) + log.Panic(err) } + + if err != nil { + log.Panicf("unable to store log location, error is: %s", err) + } + + // handle file close request + defer func(file *os.File) { + err := file.Close() + if err != nil { + log.Print(err) + } + }(file) + + // setup logging + log.SetOutput(file) log.SetPrefix("LOG: ") log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) diff --git a/pkg/helpers.go b/pkg/helpers.go index 02319b3c5..762b7703c 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -1,7 +1,9 @@ package pkg import ( + "errors" "fmt" + "github.com/kubefirst/kubefirst/configs" "io/ioutil" "log" "os" @@ -97,3 +99,31 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { return nil } + +// SetupViper handles Viper config file. If config file doesn't exist, create, in case the file is available, use it. +func SetupViper(config *configs.Config) error { + + viperConfigFile := config.KubefirstConfigFilePath + + if _, err := os.Stat(viperConfigFile); errors.Is(err, os.ErrNotExist) { + log.Printf("Config file not found, creating a blank one: %s \n", viperConfigFile) + err = os.WriteFile(viperConfigFile, []byte("createdBy: installer\n\n"), 0700) + if err != nil { + return fmt.Errorf("unable to create blank config file, error is: %s", err) + } + } + + viper.SetConfigFile(viperConfigFile) + viper.SetConfigType("yaml") + viper.AutomaticEnv() // read in environment variables that match + + // if a config file is found, read it in. + err := viper.ReadInConfig() + if err != nil { + return fmt.Errorf("unable to read config file, error is: %s", err) + } + + log.Println("Using config file:", viper.ConfigFileUsed()) + + return nil +} From 3cecb27b19889861febae0722cf8e9b03d624b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 14 Jul 2022 12:54:20 -0300 Subject: [PATCH 086/107] chore: clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/clean.go | 6 +++--- main.go | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cmd/clean.go b/cmd/clean.go index 867b12a78..c5a7c2fd5 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -19,8 +19,8 @@ import ( var cleanCmd = &cobra.Command{ Use: "clean", Short: "removes all kubefirst resources locally for new execution", - Long: `Kubefirst creates files and folders during installation at your local environment. This command removes all -Kubefirst files.`, + Long: `Kubefirst creates files and folders during installation at your local environment. This command removes and +re-create all Kubefirst files.`, Run: func(cmd *cobra.Command, args []string) { config := configs.ReadConfig() @@ -45,7 +45,7 @@ Kubefirst files.`, // remove logs folder if flag is enabled var logFolderLocation string if rmLogsFolder { - logFolderLocation = viper.GetString("log-folder-location") + logFolderLocation = viper.GetString("log.folder.location") err := os.RemoveAll(logFolderLocation) if err != nil { log.Panicf("unable to delete logs folder at %q", config.KubefirstLogPath) diff --git a/main.go b/main.go index 1bdd32e7a..d4f2f2706 100644 --- a/main.go +++ b/main.go @@ -45,10 +45,6 @@ func main() { } file, err := openLogFile(logfile) - if err != nil { - log.Panic(err) - } - if err != nil { log.Panicf("unable to store log location, error is: %s", err) } From b821488e8c035dea16cff6eab86ec30292f37171 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 14 Jul 2022 16:32:45 +0000 Subject: [PATCH 087/107] clean portforward noise Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/create.go | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index a5048a008..3ce33789a 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -12,7 +12,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "log" - "os" + "bytes" "os/exec" "syscall" "time" @@ -77,13 +77,17 @@ to quickly create a Cobra application.`, // todo this should be replaced with something more intelligent log.Println("Waiting for soft-serve installation to complete...") if !dryRun { + var kPortForwardSoftServeOutb, kPortForwardSoftServeErrb bytes.Buffer time.Sleep(60 * time.Second) kPortForwardSoftServe := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") - kPortForwardSoftServe.Stdout = os.Stdout - kPortForwardSoftServe.Stderr = os.Stderr + kPortForwardSoftServe.Stdout = &kPortForwardSoftServeOutb + kPortForwardSoftServe.Stderr = &kPortForwardSoftServeErrb err = kPortForwardSoftServe.Start() defer kPortForwardSoftServe.Process.Signal(syscall.SIGTERM) if err != nil { + // If it doesn't error, we kinda don't care much. + log.Println("Commad Execution STDOUT: %s", kPortForwardSoftServeOutb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardSoftServeErrb.String()) log.Panicf("error: failed to port-forward to soft-serve %s", err) } time.Sleep(20 * time.Second) @@ -103,12 +107,15 @@ to quickly create a Cobra application.`, informUser("ArgoCD Ready") progressPrinter.IncrementTracker("step-argo", 1) if !dryRun { + var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") - kPortForwardArgocd.Stdout = os.Stdout - kPortForwardArgocd.Stderr = os.Stderr + kPortForwardArgocd.Stdout = &kPortForwardArgocdOutb + kPortForwardArgocd.Stderr = &kPortForwardArgocdErrb err = kPortForwardArgocd.Start() defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) if err != nil { + log.Println("Commad Execution STDOUT: %s", kPortForwardArgocdOutb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardArgocdErrb.String()) log.Panicf("error: failed to port-forward to argocd in main thread %s", err) } @@ -144,12 +151,16 @@ to quickly create a Cobra application.`, waitVaultToBeInitialized(dryRun) progressPrinter.IncrementTracker("step-gitlab", 1) if !dryRun { + var kPortForwardVaultOutb, kPortForwardVaultErrb bytes.Buffer kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") - kPortForwardVault.Stdout = os.Stdout - kPortForwardVault.Stderr = os.Stderr + kPortForwardVault.Stdout = &kPortForwardVaultOutb + kPortForwardVault.Stderr = &kPortForwardVaultErrb err = kPortForwardVault.Start() defer kPortForwardVault.Process.Signal(syscall.SIGTERM) if err != nil { + // If it doesn't error, we kinda don't care much. + log.Println("Commad Execution STDOUT: %s", kPortForwardVaultOutb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardVaultErrb.String()) log.Panicf("error: failed to port-forward to vault in main thread %s", err) } } @@ -164,12 +175,16 @@ to quickly create a Cobra application.`, progressPrinter.IncrementTracker("step-gitlab", 1) if !dryRun { + var kPortForwardGitlabOutb, kPortForwardGitlabErrb bytes.Buffer kPortForwardGitlab := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") - kPortForwardGitlab.Stdout = os.Stdout - kPortForwardGitlab.Stderr = os.Stderr + kPortForwardGitlab.Stdout = &kPortForwardGitlabOutb + kPortForwardGitlab.Stderr = &kPortForwardGitlabErrb err = kPortForwardGitlab.Start() defer kPortForwardGitlab.Process.Signal(syscall.SIGTERM) if err != nil { + // If it doesn't error, we kinda don't care much. + log.Println("Commad Execution STDOUT: %s", kPortForwardGitlabOutb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardGitlabErrb.String()) log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) } } From e24141365fce1f89c92863f4d91c7bdd1ae85bec Mon Sep 17 00:00:00 2001 From: John Dietz Date: Thu, 14 Jul 2022 13:06:20 -0400 Subject: [PATCH 088/107] adding retry spec to argocd registry (#102) --- pkg/keys.go | 134 +++++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 64 deletions(-) diff --git a/pkg/keys.go b/pkg/keys.go index 2422b712d..017de752f 100644 --- a/pkg/keys.go +++ b/pkg/keys.go @@ -1,37 +1,37 @@ package pkg import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - goGitSsh "github.com/go-git/go-git/v5/plumbing/transport/ssh" - "github.com/kubefirst/kubefirst/configs" - "github.com/spf13/viper" - "golang.org/x/crypto/ssh" - "io/ioutil" - "log" - "strings" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + goGitSsh "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/kubefirst/kubefirst/configs" + "github.com/spf13/viper" + "golang.org/x/crypto/ssh" + "io/ioutil" + "log" + "strings" ) func CreateSshKeyPair() { - config := configs.ReadConfig() - publicKey := viper.GetString("botpublickey") - if publicKey == "" { - log.Println("generating new key pair") - publicKey, privateKey, _ := GenerateKey() - viper.Set("botPublicKey", publicKey) - viper.Set("botPrivateKey", privateKey) - err := viper.WriteConfig() - if err != nil { - log.Panicf("error: could not write to viper config") - } - } - publicKey = viper.GetString("botpublickey") - privateKey := viper.GetString("botprivatekey") + config := configs.ReadConfig() + publicKey := viper.GetString("botpublickey") + if publicKey == "" { + log.Println("generating new key pair") + publicKey, privateKey, _ := GenerateKey() + viper.Set("botPublicKey", publicKey) + viper.Set("botPrivateKey", privateKey) + err := viper.WriteConfig() + if err != nil { + log.Panicf("error: could not write to viper config") + } + } + publicKey = viper.GetString("botpublickey") + privateKey := viper.GetString("botprivatekey") - var argocdInitValuesYaml = []byte(fmt.Sprintf(` + var argocdInitValuesYaml = []byte(fmt.Sprintf(` server: additionalApplications: - name: registry @@ -54,6 +54,12 @@ server: selfHeal: true syncOptions: - CreateNamespace=true + retry: + limit: 5 + backoff: + duration: 5s + maxDuration: 5m0s + factor: 2 configs: repositories: soft-serve-gitops: @@ -68,57 +74,57 @@ configs: %s `, strings.ReplaceAll(privateKey, "\n", "\n "))) - err := ioutil.WriteFile(fmt.Sprintf("%s/argocd-init-values.yaml", config.K1srtFolderPath), argocdInitValuesYaml, 0644) - if err != nil { - log.Panicf("error: could not write argocd-init-values.yaml %s", err) - } + err := ioutil.WriteFile(fmt.Sprintf("%s/argocd-init-values.yaml", config.K1srtFolderPath), argocdInitValuesYaml, 0644) + if err != nil { + log.Panicf("error: could not write argocd-init-values.yaml %s", err) + } } func PublicKey() (*goGitSsh.PublicKeys, error) { - var publicKey *goGitSsh.PublicKeys - publicKey, err := goGitSsh.NewPublicKeys("gitClient", []byte(viper.GetString("botprivatekey")), "") - if err != nil { - return nil, err - } - return publicKey, err + var publicKey *goGitSsh.PublicKeys + publicKey, err := goGitSsh.NewPublicKeys("gitClient", []byte(viper.GetString("botprivatekey")), "") + if err != nil { + return nil, err + } + return publicKey, err } // GenerateKey generate public and private keys to be consumed by GitLab. func GenerateKey() (string, string, error) { - reader := rand.Reader - bitSize := 2048 + reader := rand.Reader + bitSize := 2048 - key, err := rsa.GenerateKey(reader, bitSize) - if err != nil { - return "", "", err - } + key, err := rsa.GenerateKey(reader, bitSize) + if err != nil { + return "", "", err + } - pub, err := ssh.NewPublicKey(key.Public()) - if err != nil { - return "", "", err - } - publicKey := string(ssh.MarshalAuthorizedKey(pub)) - // encode RSA key - privateKey := string(pem.EncodeToMemory( - &pem.Block{ - Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), - }, - )) + pub, err := ssh.NewPublicKey(key.Public()) + if err != nil { + return "", "", err + } + publicKey := string(ssh.MarshalAuthorizedKey(pub)) + // encode RSA key + privateKey := string(pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), + }, + )) - return publicKey, privateKey, nil + return publicKey, privateKey, nil } func ModConfigYaml() { - file, err := ioutil.ReadFile("./config.yaml") - if err != nil { - log.Println("error reading file", err) - } + file, err := ioutil.ReadFile("./config.yaml") + if err != nil { + log.Println("error reading file", err) + } - newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) + newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) - err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) - if err != nil { - panic(err) - } + err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) + if err != nil { + panic(err) + } } From 7eab4c729f7cf0dde31100e4f0bd358b6823cf30 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 14 Jul 2022 17:29:16 +0000 Subject: [PATCH 089/107] enable bucket version Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/aws/aws.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/aws/aws.go b/internal/aws/aws.go index 0128ef03c..ce44f5696 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -61,11 +61,23 @@ func BucketRand(dryRun bool, trackers map[string]*pkg.ActionTracker) { LocationConstraint: aws.String(regionName), }, }) - } + } if err != nil { log.Println("failed to create bucket "+bucketName, err.Error()) os.Exit(1) } + vc := &s3.VersioningConfiguration{} + vc.Status = aws.String(s3.BucketVersioningStatusEnabled) + versionConfigInput := &s3.PutBucketVersioningInput{ + Bucket: aws.String(bucketName), + VersioningConfiguration: vc, + } + log.Printf("[DEBUG] S3 put bucket versioning: %#v", versionConfigInput) + _, err := s3Client.PutBucketVersioning(versionConfigInput) + if err != nil { + log.Panicf("Error putting S3 versioning: %s", err) + } + } else { log.Printf("[#99] Dry-run mode, bucket creation skipped: %s", bucketName) } From 3d890c1aee04747595a01bbc098ddae4c01d3b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 14 Jul 2022 14:34:12 -0300 Subject: [PATCH 090/107] refactor: update k1srt folder to k1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/clean.go | 14 +-- cmd/create.go | 52 +++++------ cmd/info.go | 2 +- cmd/init.go | 2 +- cmd/kubefirstTemplate.go | 8 +- configs/config.go | 12 +-- configs/kubefirstDirectory.go | 8 +- internal/downloadManager/download.go | 8 +- internal/gitClient/git.go | 6 +- internal/gitlab/gitlab.go | 45 +++++----- internal/helm/helm.go | 2 +- internal/softserve/softserve.go | 6 +- internal/terraform/terraform.go | 24 ++--- internal/vault/vault.go | 42 ++++----- pkg/keys.go | 128 +++++++++++++-------------- 15 files changed, 176 insertions(+), 183 deletions(-) diff --git a/cmd/clean.go b/cmd/clean.go index c5a7c2fd5..91850e547 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -32,9 +32,9 @@ re-create all Kubefirst files.`, } // delete files and folders - err = os.RemoveAll(config.K1srtFolderPath) + err = os.RemoveAll(config.K1FolderPath) if err != nil { - log.Panicf("unable to delete %q folder, error is: %s", config.K1srtFolderPath, err) + log.Panicf("unable to delete %q folder, error is: %s", config.K1FolderPath, err) } err = os.Remove(config.KubefirstConfigFilePath) @@ -53,12 +53,12 @@ re-create all Kubefirst files.`, } // re-create folder - if err := os.Mkdir(fmt.Sprintf("%s", config.K1srtFolderPath), os.ModePerm); err != nil { - log.Panicf("error: could not create directory %q - it must exist to continue. error is: %s", config.K1srtFolderPath, err) + if err := os.Mkdir(fmt.Sprintf("%s", config.K1FolderPath), os.ModePerm); err != nil { + log.Panicf("error: could not create directory %q - it must exist to continue. error is: %s", config.K1FolderPath, err) } // re-create base - log.Printf("%q config file and %q folder were deleted and re-created", config.KubefirstConfigFilePath, config.K1srtFolderPath) + log.Printf("%q config file and %q folder were deleted and re-created", config.KubefirstConfigFilePath, config.K1FolderPath) var cleanSummary bytes.Buffer cleanSummary.WriteString(strings.Repeat("-", 70)) @@ -67,14 +67,14 @@ re-create all Kubefirst files.`, cleanSummary.WriteString("\n\nFiles and folders deleted:\n\n") cleanSummary.WriteString(fmt.Sprintf(" %q\n", config.KubefirstConfigFilePath)) - cleanSummary.WriteString(fmt.Sprintf(" %q\n", config.K1srtFolderPath)) + cleanSummary.WriteString(fmt.Sprintf(" %q\n", config.K1FolderPath)) if rmLogsFolder { cleanSummary.WriteString(fmt.Sprintf(" %q\n", logFolderLocation)) } cleanSummary.WriteString("\nRe-created empty folder: \n\n") - cleanSummary.WriteString(fmt.Sprintf(" %q\n\n", config.K1srtFolderPath)) + cleanSummary.WriteString(fmt.Sprintf(" %q\n\n", config.K1FolderPath)) cleanSummary.WriteString("Re-created empty config file: \n\n") cleanSummary.WriteString(fmt.Sprintf(" %q", config.KubefirstConfigFilePath)) diff --git a/cmd/create.go b/cmd/create.go index 3ce33789a..20138f6b6 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -1,22 +1,22 @@ package cmd import ( + "bytes" "fmt" "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/internal/argocd" "github.com/kubefirst/kubefirst/internal/gitlab" "github.com/kubefirst/kubefirst/internal/helm" + "github.com/kubefirst/kubefirst/internal/progressPrinter" "github.com/kubefirst/kubefirst/internal/softserve" "github.com/kubefirst/kubefirst/internal/terraform" "github.com/kubefirst/kubefirst/internal/vault" "github.com/spf13/cobra" "github.com/spf13/viper" "log" - "bytes" "os/exec" "syscall" "time" - "github.com/kubefirst/kubefirst/internal/progressPrinter" ) const trackerStage20 = "0 - Apply Base" @@ -55,13 +55,11 @@ to quickly create a Cobra application.`, infoCmd.Run(cmd, args) progressPrinter.IncrementTracker("step-0", 1) - progressPrinter.AddTracker("step-softserve", "Prepare Temporary Repo ", 4) sendStartedInstallTelemetry(dryRun) progressPrinter.IncrementTracker("step-softserve", 1) - - directory := fmt.Sprintf("%s/gitops/terraform/base", config.K1srtFolderPath) + directory := fmt.Sprintf("%s/gitops/terraform/base", config.K1FolderPath) informUser("Creating K8S Cluster") terraform.ApplyBaseTerraform(dryRun, directory) progressPrinter.IncrementTracker("step-softserve", 1) @@ -75,9 +73,9 @@ to quickly create a Cobra application.`, waitForNamespaceandPods(dryRun, config, "soft-serve", "app=soft-serve") progressPrinter.IncrementTracker("step-softserve", 1) // todo this should be replaced with something more intelligent - log.Println("Waiting for soft-serve installation to complete...") + log.Println("Waiting for soft-serve installation to complete...") if !dryRun { - var kPortForwardSoftServeOutb, kPortForwardSoftServeErrb bytes.Buffer + var kPortForwardSoftServeOutb, kPortForwardSoftServeErrb bytes.Buffer time.Sleep(60 * time.Second) kPortForwardSoftServe := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "soft-serve", "port-forward", "svc/soft-serve", "8022:22") kPortForwardSoftServe.Stdout = &kPortForwardSoftServeOutb @@ -85,7 +83,7 @@ to quickly create a Cobra application.`, err = kPortForwardSoftServe.Start() defer kPortForwardSoftServe.Process.Signal(syscall.SIGTERM) if err != nil { - // If it doesn't error, we kinda don't care much. + // If it doesn't error, we kinda don't care much. log.Println("Commad Execution STDOUT: %s", kPortForwardSoftServeOutb.String()) log.Println("Commad Execution STDERR: %s", kPortForwardSoftServeErrb.String()) log.Panicf("error: failed to port-forward to soft-serve %s", err) @@ -94,8 +92,8 @@ to quickly create a Cobra application.`, } informUser("Softserve Update") - softserve.ConfigureSoftServeAndPush(dryRun) - progressPrinter.IncrementTracker("step-softserve", 1) + softserve.ConfigureSoftServeAndPush(dryRun) + progressPrinter.IncrementTracker("step-softserve", 1) progressPrinter.AddTracker("step-argo", "Deploy CI/CD ", 5) informUser("Deploy ArgoCD") @@ -107,7 +105,7 @@ to quickly create a Cobra application.`, informUser("ArgoCD Ready") progressPrinter.IncrementTracker("step-argo", 1) if !dryRun { - var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer + var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") kPortForwardArgocd.Stdout = &kPortForwardArgocdOutb kPortForwardArgocd.Stderr = &kPortForwardArgocdErrb @@ -126,7 +124,7 @@ to quickly create a Cobra application.`, progressPrinter.IncrementTracker("step-argo", 1) informUser("Setting argocd credentials") - setArgocdCreds(dryRun) + setArgocdCreds(dryRun) progressPrinter.IncrementTracker("step-argo", 1) informUser("Getting an argocd auth token") @@ -148,17 +146,17 @@ to quickly create a Cobra application.`, //! progressPrinter.AddTracker("step-gitlab", "Setup Gitlab", 6) informUser("Waiting vault to be ready") - waitVaultToBeInitialized(dryRun) + waitVaultToBeInitialized(dryRun) progressPrinter.IncrementTracker("step-gitlab", 1) if !dryRun { - var kPortForwardVaultOutb, kPortForwardVaultErrb bytes.Buffer + var kPortForwardVaultOutb, kPortForwardVaultErrb bytes.Buffer kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") kPortForwardVault.Stdout = &kPortForwardVaultOutb kPortForwardVault.Stderr = &kPortForwardVaultErrb err = kPortForwardVault.Start() defer kPortForwardVault.Process.Signal(syscall.SIGTERM) if err != nil { - // If it doesn't error, we kinda don't care much. + // If it doesn't error, we kinda don't care much. log.Println("Commad Execution STDOUT: %s", kPortForwardVaultOutb.String()) log.Println("Commad Execution STDERR: %s", kPortForwardVaultErrb.String()) log.Panicf("error: failed to port-forward to vault in main thread %s", err) @@ -175,14 +173,14 @@ to quickly create a Cobra application.`, progressPrinter.IncrementTracker("step-gitlab", 1) if !dryRun { - var kPortForwardGitlabOutb, kPortForwardGitlabErrb bytes.Buffer + var kPortForwardGitlabOutb, kPortForwardGitlabErrb bytes.Buffer kPortForwardGitlab := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") kPortForwardGitlab.Stdout = &kPortForwardGitlabOutb kPortForwardGitlab.Stderr = &kPortForwardGitlabErrb err = kPortForwardGitlab.Start() defer kPortForwardGitlab.Process.Signal(syscall.SIGTERM) if err != nil { - // If it doesn't error, we kinda don't care much. + // If it doesn't error, we kinda don't care much. log.Println("Commad Execution STDOUT: %s", kPortForwardGitlabOutb.String()) log.Println("Commad Execution STDERR: %s", kPortForwardGitlabErrb.String()) log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) @@ -210,13 +208,13 @@ to quickly create a Cobra application.`, /** */ - waitVaultToBeInitialized(dryRun) + waitVaultToBeInitialized(dryRun) informUser("Vault initialized") progressPrinter.IncrementTracker("step-vault", 1) - waitForVaultUnseal(dryRun,config) + waitForVaultUnseal(dryRun, config) informUser("Vault unseal") - progressPrinter.IncrementTracker("step-vault", 1) + progressPrinter.IncrementTracker("step-vault", 1) log.Println("configuring vault") vault.ConfigureVault(dryRun) @@ -228,35 +226,32 @@ to quickly create a Cobra application.`, informUser("Vault secret created") progressPrinter.IncrementTracker("step-vault", 1) - progressPrinter.AddTracker("step-post-gitlab", "Finalize Gitlab updates", 5) vault.AddGitlabOidcApplications(dryRun) informUser("Added Gitlab OIDC") progressPrinter.IncrementTracker("step-post-gitlab", 1) - informUser("Waiting for Gitlab dns to propagate before continuing") gitlab.AwaitGitlab(dryRun) - progressPrinter.IncrementTracker("step-post-gitlab", 1) - + progressPrinter.IncrementTracker("step-post-gitlab", 1) informUser("Pushing gitops repo to origin gitlab") // refactor: sounds like a new functions, should PushGitOpsToGitLab be renamed/update signature? gitlab.PushGitRepo(dryRun, config, "gitlab", "gitops") // todo: need to handle if this was already pushed, errors on failure) - progressPrinter.IncrementTracker("step-post-gitlab", 1) + progressPrinter.IncrementTracker("step-post-gitlab", 1) // todo: keep one of the two git push functions, they're similar, but not exactly the same //gitlab.PushGitOpsToGitLab(dryRun) informUser("Pushing metaphor repo to origin gitlab") gitlab.PushGitRepo(dryRun, config, "gitlab", "metaphor") - progressPrinter.IncrementTracker("step-post-gitlab", 1) + progressPrinter.IncrementTracker("step-post-gitlab", 1) // todo: keep one of the two git push functions, they're similar, but not exactly the same //gitlab.PushGitOpsToGitLab(dryRun) informUser("Changing registry to Gitlab") gitlab.ChangeRegistryToGitLab(dryRun) - progressPrinter.IncrementTracker("step-post-gitlab", 1) + progressPrinter.IncrementTracker("step-post-gitlab", 1) // todo triage / force apply the contents adjusting // todo kind: Application .repoURL: @@ -275,8 +270,7 @@ func init() { createCmd.Flags().Bool("dry-run", false, "set to dry-run mode, no changes done on cloud provider selected") createCmd.Flags().Bool("skip-gitlab", false, "Skip GitLab lab install and vault setup") createCmd.Flags().Bool("skip-vault", false, "Skip post-gitClient lab install and vault setup") - + progressPrinter.GetInstance() progressPrinter.SetupProgress(4) } - diff --git a/cmd/info.go b/cmd/info.go index ad64f6a6f..c2467166c 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -22,7 +22,7 @@ var infoCmd = &cobra.Command{ fmt.Printf("Architecture: %s\n", config.LocalArchitecture) fmt.Printf("Go Lang version: v%s \n", runtime.Version()) fmt.Printf("Kubefirst config file: %s\n", config.KubefirstConfigFilePath) - fmt.Printf("Kubefirst config folder: %s\n", config.K1srtFolderPath) + fmt.Printf("Kubefirst config folder: %s\n", config.K1FolderPath) fmt.Printf("Kubectl path: %s\n", config.KubectlClientPath) fmt.Printf("Terraform path: %s\n", config.TerraformPath) fmt.Printf("Kubeconfig path: %s\n", config.KubeConfigPath) diff --git a/cmd/init.go b/cmd/init.go index 93d58262c..aaa595ce0 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -205,7 +205,7 @@ func init() { if err != nil { log.Panic(err) } - initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.k1srt") + initCmd.Flags().Bool("clean", false, "delete any local kubefirst content ~/.kubefirst, ~/.k1") log.SetPrefix("LOG: ") log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 897dac99d..53e012ef8 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -23,7 +23,7 @@ func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName st } repoUrl := fmt.Sprintf("https://github.com/%s/%s-template", githubOrg, repoName) - directory := fmt.Sprintf("%s/%s", config.K1srtFolderPath, repoName) + directory := fmt.Sprintf("%s/%s", config.K1FolderPath, repoName) log.Println("git clone", repoUrl, directory) log.Println("git clone -b ", branch, repoUrl, directory) @@ -38,11 +38,11 @@ func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName st viper.Set(fmt.Sprintf("init.repos.%s.cloned", repoName), true) viper.WriteConfig() - log.Printf("cloned %s-template repository to directory %s/%s", repoName, config.K1srtFolderPath, repoName) + log.Printf("cloned %s-template repository to directory %s/%s", repoName, config.K1FolderPath, repoName) - log.Printf("detokenizing %s/%s", config.K1srtFolderPath, repoName) + log.Printf("detokenizing %s/%s", config.K1FolderPath, repoName) detokenize(directory) - log.Printf("detokenization of %s/%s complete", config.K1srtFolderPath, repoName) + log.Printf("detokenization of %s/%s complete", config.K1FolderPath, repoName) viper.Set(fmt.Sprintf("init.repos.%s.detokenized", repoName), true) viper.WriteConfig() diff --git a/configs/config.go b/configs/config.go index 06b0de70c..89e47fdac 100644 --- a/configs/config.go +++ b/configs/config.go @@ -23,7 +23,7 @@ type Config struct { KubefirstLogPath string `env:"KUBEFIRST_LOG_PATH" envDefault:"logs"` KubefirstConfigFilePath string - K1srtFolderPath string + K1FolderPath string KubectlClientPath string KubeConfigPath string HelmClientPath string @@ -50,7 +50,7 @@ func ReadConfig() *Config { log.Panic(err) } - config.K1srtFolderPath = fmt.Sprintf("%s/.k1srt", homePath) + config.K1FolderPath = fmt.Sprintf("%s/.k1", homePath) if err != nil { log.Panic(err) } @@ -60,10 +60,10 @@ func ReadConfig() *Config { config.LocalOs = runtime.GOOS config.LocalArchitecture = runtime.GOARCH - config.KubectlClientPath = fmt.Sprintf("%s/tools/kubectl", config.K1srtFolderPath) - config.KubeConfigPath = fmt.Sprintf("%s/gitops/terraform/base/kubeconfig_kubefirst", config.K1srtFolderPath) - config.TerraformPath = fmt.Sprintf("%s/tools/terraform", config.K1srtFolderPath) - config.HelmClientPath = fmt.Sprintf("%s/tools/helm", config.K1srtFolderPath) + config.KubectlClientPath = fmt.Sprintf("%s/tools/kubectl", config.K1FolderPath) + config.KubeConfigPath = fmt.Sprintf("%s/gitops/terraform/base/kubeconfig_kubefirst", config.K1FolderPath) + config.TerraformPath = fmt.Sprintf("%s/tools/terraform", config.K1FolderPath) + config.HelmClientPath = fmt.Sprintf("%s/tools/helm", config.K1FolderPath) config.TerraformVersion = "1.0.11" diff --git a/configs/kubefirstDirectory.go b/configs/kubefirstDirectory.go index 87895b40f..ef1291a75 100755 --- a/configs/kubefirstDirectory.go +++ b/configs/kubefirstDirectory.go @@ -6,15 +6,15 @@ import ( "os" ) -// CheckKubefirstDir validate if ~/.k1srt directory is ready to be used +// CheckKubefirstDir validate if ~/.k1 directory is ready to be used func CheckKubefirstDir(config *Config) error { - k1sDir := fmt.Sprintf("%s", config.K1srtFolderPath) + k1sDir := fmt.Sprintf("%s", config.K1FolderPath) if _, err := os.Stat(k1sDir); err != nil { - errorMsg := fmt.Sprintf("unable to load \".k1srt\" directory, error is: %s", err) + errorMsg := fmt.Sprintf("unable to load \".k1\" directory, error is: %s", err) log.Println(errorMsg) return fmt.Errorf(errorMsg) } - log.Printf("\".k1srt\" directory found: %s", k1sDir) + log.Printf("\".k1\" directory found: %s", k1sDir) return nil } diff --git a/internal/downloadManager/download.go b/internal/downloadManager/download.go index 22775083c..42ac51ff1 100644 --- a/internal/downloadManager/download.go +++ b/internal/downloadManager/download.go @@ -17,7 +17,7 @@ import ( func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracker) error { - toolsDir := fmt.Sprintf("%s/tools", config.K1srtFolderPath) + toolsDir := fmt.Sprintf("%s/tools", config.K1FolderPath) err := os.Mkdir(toolsDir, 0777) if err != nil { @@ -67,14 +67,14 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke config.LocalArchitecture, ) - terraformDownloadZipPath := fmt.Sprintf("%s/tools/terraform.zip", config.K1srtFolderPath) + terraformDownloadZipPath := fmt.Sprintf("%s/tools/terraform.zip", config.K1FolderPath) err = downloadFile(terraformDownloadZipPath, terraformDownloadUrl) if err != nil { log.Println("error reading terraform file") return err } - unzipDirectory := fmt.Sprintf("%s/tools", config.K1srtFolderPath) + unzipDirectory := fmt.Sprintf("%s/tools", config.K1FolderPath) unzip(terraformDownloadZipPath, unzipDirectory) err = os.Chmod(unzipDirectory, 0777) @@ -98,7 +98,7 @@ func DownloadTools(config *configs.Config, trackers map[string]*pkg.ActionTracke config.LocalArchitecture, ) - helmDownloadTarGzPath := fmt.Sprintf("%s/tools/helm.tar.gz", config.K1srtFolderPath) + helmDownloadTarGzPath := fmt.Sprintf("%s/tools/helm.tar.gz", config.K1FolderPath) err = downloadFile(helmDownloadTarGzPath, helmDownloadUrl) if err != nil { return err diff --git a/internal/gitClient/git.go b/internal/gitClient/git.go index 852011153..19e824b06 100644 --- a/internal/gitClient/git.go +++ b/internal/gitClient/git.go @@ -19,7 +19,7 @@ func CloneGitOpsRepo() { config := configs.ReadConfig() url := "https://github.com/kubefirst/gitops-template" - directory := fmt.Sprintf("%s/gitops", config.K1srtFolderPath) + directory := fmt.Sprintf("%s/gitops", config.K1FolderPath) versionGitOps := viper.GetString("version-gitops") @@ -34,13 +34,13 @@ func CloneGitOpsRepo() { log.Panicf("error cloning gitops-template repository from github, error is: %s", err) } - log.Println("downloaded gitops repo from template to directory", config.K1srtFolderPath, "/gitops") + log.Println("downloaded gitops repo from template to directory", config.K1FolderPath, "/gitops") } func PushGitopsToSoftServe() { cfg := configs.ReadConfig() - directory := fmt.Sprintf("%s/gitops", cfg.K1srtFolderPath) + directory := fmt.Sprintf("%s/gitops", cfg.K1FolderPath) log.Println("open gitClient repo", directory) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index c1468290d..11389dbf9 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -93,8 +93,8 @@ func PushGitOpsToGitLab(dryRun bool) { //TODO: should this step to be skipped if already executed? domain := viper.GetString("aws.hostedzonename") - pkg.Detokenize(fmt.Sprintf("%s/gitops", cfg.K1srtFolderPath)) - directory := fmt.Sprintf("%s/gitops", cfg.K1srtFolderPath) + pkg.Detokenize(fmt.Sprintf("%s/gitops", cfg.K1FolderPath)) + directory := fmt.Sprintf("%s/gitops", cfg.K1FolderPath) repo, err := git.PlainOpen(directory) if err != nil { @@ -252,18 +252,18 @@ func ApplyGitlabTerraform(dryRun bool, directory string) { //* AWS_SDK_LOAD_CONFIG=1 //* https://registry.terraform.io/providers/hashicorp/aws/2.34.0/docs#shared-credentials-file envs := map[string]string{} - envs["AWS_SDK_LOAD_CONFIG"]="1" - envs["AWS_PROFILE"]=config.AwsProfile + envs["AWS_SDK_LOAD_CONFIG"] = "1" + envs["AWS_PROFILE"] = config.AwsProfile // Prepare for terraform gitlab execution - envs["GITLAB_TOKEN"]=viper.GetString("gitlab.token") - envs["GITLAB_BASE_URL"]=viper.GetString("gitlab.local.service") + envs["GITLAB_TOKEN"] = viper.GetString("gitlab.token") + envs["GITLAB_BASE_URL"] = viper.GetString("gitlab.local.service") - directory = fmt.Sprintf("%s/gitops/terraform/gitlab", config.K1srtFolderPath) + directory = fmt.Sprintf("%s/gitops/terraform/gitlab", config.K1FolderPath) err := os.Chdir(directory) if err != nil { log.Panic("error: could not change directory to " + directory) } - err = pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "init") if err != nil { log.Panicf("error: terraform init for gitlab failed %s", err) } @@ -325,17 +325,16 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { config := configs.ReadConfig() envs := map[string]string{} - envs["AWS_REGION"]= viper.GetString("aws.region") - envs["AWS_ACCOUNT_ID"]= viper.GetString("aws.accountid") - envs["HOSTED_ZONE_NAME"]= viper.GetString("aws.hostedzonename") - envs["GITLAB_TOKEN"]= viper.GetString("gitlab.token") - - envs["TF_VAR_aws_account_id"]= viper.GetString("aws.accountid") - envs["TF_VAR_aws_region"]= viper.GetString("aws.region") - envs["TF_VAR_hosted_zone_name"]= viper.GetString("aws.hostedzonename") + envs["AWS_REGION"] = viper.GetString("aws.region") + envs["AWS_ACCOUNT_ID"] = viper.GetString("aws.accountid") + envs["HOSTED_ZONE_NAME"] = viper.GetString("aws.hostedzonename") + envs["GITLAB_TOKEN"] = viper.GetString("gitlab.token") + envs["TF_VAR_aws_account_id"] = viper.GetString("aws.accountid") + envs["TF_VAR_aws_region"] = viper.GetString("aws.region") + envs["TF_VAR_hosted_zone_name"] = viper.GetString("aws.hostedzonename") - directory := fmt.Sprintf("%s/.gitops/terraform/gitlab", config.K1srtFolderPath) + directory := fmt.Sprintf("%s/.gitops/terraform/gitlab", config.K1FolderPath) err := os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) @@ -349,7 +348,7 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { log.Panicf("failed to terraform init gitlab %s", err) } - err = pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "destroy", "-auto-approve") if err != nil { log.Panicf("failed to terraform destroy gitlab %s", err) } @@ -452,7 +451,7 @@ func ChangeRegistryToGitLab(dryRun bool) { log.Panicf("error creating argocd repository connection secret %s", err) } - k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.K1srtFolderPath)) + k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.K1FolderPath)) k.Stdout = os.Stdout k.Stderr = os.Stderr err = k.Run() @@ -476,7 +475,7 @@ func HydrateGitlabMetaphorRepo(dryRun bool) { return } - metaphorTemplateDir := fmt.Sprintf("%s/metaphor", cfg.K1srtFolderPath) + metaphorTemplateDir := fmt.Sprintf("%s/metaphor", cfg.K1FolderPath) url := "https://github.com/kubefirst/metaphor-template" @@ -535,9 +534,9 @@ func HydrateGitlabMetaphorRepo(dryRun bool) { func PushGitRepo(dryRun bool, config *configs.Config, gitOrigin, repoName string) { if dryRun { log.Printf("[#99] Dry-run mode, PushGitRepo skipped.") - return - } - repoDir := fmt.Sprintf("%s/%s", config.K1srtFolderPath, repoName) + return + } + repoDir := fmt.Sprintf("%s/%s", config.K1FolderPath, repoName) repo, err := git.PlainOpen(repoDir) if err != nil { log.Panicf("error opening repo %s: %s", repoName, err) diff --git a/internal/helm/helm.go b/internal/helm/helm.go index 9754a972a..c9731c538 100644 --- a/internal/helm/helm.go +++ b/internal/helm/helm.go @@ -33,7 +33,7 @@ func InstallArgocd(dryRun bool) { log.Panicf("error: could not helm repo update %s", err) } - helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/argocd-init-values.yaml", config.K1srtFolderPath), "argo/argo-cd") + helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/argocd-init-values.yaml", config.K1FolderPath), "argo/argo-cd") helmInstallArgocdCmd.Stdout = os.Stdout helmInstallArgocdCmd.Stderr = os.Stderr err = helmInstallArgocdCmd.Run() diff --git a/internal/softserve/softserve.go b/internal/softserve/softserve.go index 92d645a36..95f9cf7c4 100644 --- a/internal/softserve/softserve.go +++ b/internal/softserve/softserve.go @@ -24,7 +24,7 @@ func CreateSoftServe(dryRun bool, kubeconfigPath string) { return } - softServePath := fmt.Sprintf("%s/gitops/components/soft-serve/manifests.yaml", config.K1srtFolderPath) + softServePath := fmt.Sprintf("%s/gitops/components/soft-serve/manifests.yaml", config.K1FolderPath) softServeApplyOut, softServeApplyErr, errSoftServeApply := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", kubeconfigPath, "-n", "soft-serve", "apply", "-f", softServePath, "--wait") log.Printf("Result:\n\t%s\n\t%s\n", softServeApplyOut, softServeApplyErr) if errSoftServeApply != nil { @@ -68,7 +68,7 @@ func configureSoftServe() { config := configs.ReadConfig() url := "ssh://127.0.0.1:8022/config" - directory := fmt.Sprintf("%s/config", config.K1srtFolderPath) + directory := fmt.Sprintf("%s/config", config.K1FolderPath) log.Println("gitClient clone", url, directory) @@ -96,7 +96,7 @@ func configureSoftServe() { log.Panic(err) } - println("re-wrote config.yaml", config.K1srtFolderPath, "/config") + println("re-wrote config.yaml", config.K1FolderPath, "/config") w, _ := repo.Worktree() diff --git a/internal/terraform/terraform.go b/internal/terraform/terraform.go index 56c859783..bd64f4bc5 100644 --- a/internal/terraform/terraform.go +++ b/internal/terraform/terraform.go @@ -22,19 +22,19 @@ func ApplyBaseTerraform(dryRun bool, directory string) { return } envs := map[string]string{} - envs["TF_VAR_aws_account_id"]=viper.GetString("aws.accountid") - envs["TF_VAR_aws_region"]= viper.GetString("aws.region") - envs["TF_VAR_hosted_zone_name"]=viper.GetString("aws.hostedzonename") + envs["TF_VAR_aws_account_id"] = viper.GetString("aws.accountid") + envs["TF_VAR_aws_region"] = viper.GetString("aws.region") + envs["TF_VAR_hosted_zone_name"] = viper.GetString("aws.hostedzonename") err := os.Chdir(directory) if err != nil { log.Panicf("error, directory does not exist - did you `kubefirst init`?: %s \nerror: %v", directory, err) } - err = pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "init") if err != nil { log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) } - err = pkg.ExecShellWithVars(envs,config.TerraformPath, "apply", "-auto-approve") + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "apply", "-auto-approve") if err != nil { log.Panic(fmt.Sprintf("error: terraform init failed %v", err)) } @@ -54,7 +54,7 @@ func ApplyBaseTerraform(dryRun bool, directory string) { viper.Set("vault.kmskeyid", keyId) viper.Set("create.terraformapplied.base", true) viper.WriteConfig() - pkg.Detokenize(fmt.Sprintf("%s/gitops", config.K1srtFolderPath)) + pkg.Detokenize(fmt.Sprintf("%s/gitops", config.K1FolderPath)) } else { log.Println("Skipping: ApplyBaseTerraform") } @@ -63,23 +63,23 @@ func ApplyBaseTerraform(dryRun bool, directory string) { func DestroyBaseTerraform(skipBaseTerraform bool) { config := configs.ReadConfig() if !skipBaseTerraform { - directory := fmt.Sprintf("%s/gitops/terraform/base", config.K1srtFolderPath) + directory := fmt.Sprintf("%s/gitops/terraform/base", config.K1FolderPath) err := os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) } envs := map[string]string{} - envs["TF_VAR_aws_account_id"]=viper.GetString("aws.accountid") - envs["TF_VAR_aws_region"]= viper.GetString("aws.region") - envs["TF_VAR_hosted_zone_name"]=viper.GetString("aws.hostedzonename") + envs["TF_VAR_aws_account_id"] = viper.GetString("aws.accountid") + envs["TF_VAR_aws_region"] = viper.GetString("aws.region") + envs["TF_VAR_hosted_zone_name"] = viper.GetString("aws.hostedzonename") - err = pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "init") if err != nil { log.Panicf("failed to terraform init base %v", err) } - err = pkg.ExecShellWithVars(envs,config.TerraformPath, "destroy", "-auto-approve") + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "destroy", "-auto-approve") if err != nil { log.Panicf("failed to terraform destroy base %v", err) } diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 3853b75b2..5bb7589da 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -6,8 +6,8 @@ import ( "fmt" vault "github.com/hashicorp/vault/api" "github.com/kubefirst/kubefirst/configs" - "github.com/kubefirst/kubefirst/pkg" "github.com/kubefirst/kubefirst/internal/k8s" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" gitlab "github.com/xanzy/go-gitlab" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -89,35 +89,35 @@ func ConfigureVault(dryRun bool) { // Prepare for terraform vault execution envs := map[string]string{} - envs["VAULT_ADDR"]="http://localhost:8200" //Should this come from init? - envs["VAULT_TOKEN"]=vaultToken - envs["AWS_SDK_LOAD_CONFIG"]= "1" - envs["AWS_PROFILE"]=config.AwsProfile - envs["AWS_DEFAULT_REGION"]= viper.GetString("aws.region") - - envs["TF_VAR_vault_addr"]= fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename")) - envs["TF_VAR_aws_account_id"]= viper.GetString("aws.accountid") - envs["TF_VAR_aws_region"]= viper.GetString("aws.region") - envs["TF_VAR_email_address"]= viper.GetString("adminemail") - envs["TF_VAR_gitlab_runner_token"]= viper.GetString("gitlab.runnertoken") - envs["TF_VAR_gitlab_token"]= viper.GetString("gitlab.token") - envs["TF_VAR_hosted_zone_id"]= viper.GetString("aws.domainid") - envs["TF_VAR_hosted_zone_name"]= viper.GetString("aws.hostedzonename") - envs["TF_VAR_vault_token"]= vaultToken - envs["TF_VAR_vault_redirect_uris"]= "[\"will-be-patched-later\"]" - - directory := fmt.Sprintf("%s/gitops/terraform/vault", config.K1srtFolderPath) + envs["VAULT_ADDR"] = "http://localhost:8200" //Should this come from init? + envs["VAULT_TOKEN"] = vaultToken + envs["AWS_SDK_LOAD_CONFIG"] = "1" + envs["AWS_PROFILE"] = config.AwsProfile + envs["AWS_DEFAULT_REGION"] = viper.GetString("aws.region") + + envs["TF_VAR_vault_addr"] = fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename")) + envs["TF_VAR_aws_account_id"] = viper.GetString("aws.accountid") + envs["TF_VAR_aws_region"] = viper.GetString("aws.region") + envs["TF_VAR_email_address"] = viper.GetString("adminemail") + envs["TF_VAR_gitlab_runner_token"] = viper.GetString("gitlab.runnertoken") + envs["TF_VAR_gitlab_token"] = viper.GetString("gitlab.token") + envs["TF_VAR_hosted_zone_id"] = viper.GetString("aws.domainid") + envs["TF_VAR_hosted_zone_name"] = viper.GetString("aws.hostedzonename") + envs["TF_VAR_vault_token"] = vaultToken + envs["TF_VAR_vault_redirect_uris"] = "[\"will-be-patched-later\"]" + + directory := fmt.Sprintf("%s/gitops/terraform/vault", config.K1FolderPath) err = os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) } - err = pkg.ExecShellWithVars(envs,config.TerraformPath, "init") + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "init") if err != nil { log.Panicf("error: terraform init failed %s", err) } - err = pkg.ExecShellWithVars(envs,config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") + err = pkg.ExecShellWithVars(envs, config.TerraformPath, "apply", "-target", "module.bootstrap", "-auto-approve") if err != nil { log.Panicf("error: terraform apply failed %s", err) } diff --git a/pkg/keys.go b/pkg/keys.go index 017de752f..21c4caf46 100644 --- a/pkg/keys.go +++ b/pkg/keys.go @@ -1,37 +1,37 @@ package pkg import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "fmt" - goGitSsh "github.com/go-git/go-git/v5/plumbing/transport/ssh" - "github.com/kubefirst/kubefirst/configs" - "github.com/spf13/viper" - "golang.org/x/crypto/ssh" - "io/ioutil" - "log" - "strings" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" + goGitSsh "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/kubefirst/kubefirst/configs" + "github.com/spf13/viper" + "golang.org/x/crypto/ssh" + "io/ioutil" + "log" + "strings" ) func CreateSshKeyPair() { - config := configs.ReadConfig() - publicKey := viper.GetString("botpublickey") - if publicKey == "" { - log.Println("generating new key pair") - publicKey, privateKey, _ := GenerateKey() - viper.Set("botPublicKey", publicKey) - viper.Set("botPrivateKey", privateKey) - err := viper.WriteConfig() - if err != nil { - log.Panicf("error: could not write to viper config") - } - } - publicKey = viper.GetString("botpublickey") - privateKey := viper.GetString("botprivatekey") + config := configs.ReadConfig() + publicKey := viper.GetString("botpublickey") + if publicKey == "" { + log.Println("generating new key pair") + publicKey, privateKey, _ := GenerateKey() + viper.Set("botPublicKey", publicKey) + viper.Set("botPrivateKey", privateKey) + err := viper.WriteConfig() + if err != nil { + log.Panicf("error: could not write to viper config") + } + } + publicKey = viper.GetString("botpublickey") + privateKey := viper.GetString("botprivatekey") - var argocdInitValuesYaml = []byte(fmt.Sprintf(` + var argocdInitValuesYaml = []byte(fmt.Sprintf(` server: additionalApplications: - name: registry @@ -74,57 +74,57 @@ configs: %s `, strings.ReplaceAll(privateKey, "\n", "\n "))) - err := ioutil.WriteFile(fmt.Sprintf("%s/argocd-init-values.yaml", config.K1srtFolderPath), argocdInitValuesYaml, 0644) - if err != nil { - log.Panicf("error: could not write argocd-init-values.yaml %s", err) - } + err := ioutil.WriteFile(fmt.Sprintf("%s/argocd-init-values.yaml", config.K1FolderPath), argocdInitValuesYaml, 0644) + if err != nil { + log.Panicf("error: could not write argocd-init-values.yaml %s", err) + } } func PublicKey() (*goGitSsh.PublicKeys, error) { - var publicKey *goGitSsh.PublicKeys - publicKey, err := goGitSsh.NewPublicKeys("gitClient", []byte(viper.GetString("botprivatekey")), "") - if err != nil { - return nil, err - } - return publicKey, err + var publicKey *goGitSsh.PublicKeys + publicKey, err := goGitSsh.NewPublicKeys("gitClient", []byte(viper.GetString("botprivatekey")), "") + if err != nil { + return nil, err + } + return publicKey, err } // GenerateKey generate public and private keys to be consumed by GitLab. func GenerateKey() (string, string, error) { - reader := rand.Reader - bitSize := 2048 + reader := rand.Reader + bitSize := 2048 - key, err := rsa.GenerateKey(reader, bitSize) - if err != nil { - return "", "", err - } + key, err := rsa.GenerateKey(reader, bitSize) + if err != nil { + return "", "", err + } - pub, err := ssh.NewPublicKey(key.Public()) - if err != nil { - return "", "", err - } - publicKey := string(ssh.MarshalAuthorizedKey(pub)) - // encode RSA key - privateKey := string(pem.EncodeToMemory( - &pem.Block{ - Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), - }, - )) + pub, err := ssh.NewPublicKey(key.Public()) + if err != nil { + return "", "", err + } + publicKey := string(ssh.MarshalAuthorizedKey(pub)) + // encode RSA key + privateKey := string(pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), + }, + )) - return publicKey, privateKey, nil + return publicKey, privateKey, nil } func ModConfigYaml() { - file, err := ioutil.ReadFile("./config.yaml") - if err != nil { - log.Println("error reading file", err) - } + file, err := ioutil.ReadFile("./config.yaml") + if err != nil { + log.Println("error reading file", err) + } - newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) + newFile := strings.Replace(string(file), "allow-keyless: false", "allow-keyless: true", -1) - err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) - if err != nil { - panic(err) - } + err = ioutil.WriteFile("./config.yaml", []byte(newFile), 0) + if err != nil { + panic(err) + } } From f5e963c36f9c9cb2cd7d643e17f6201a26e461bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 14 Jul 2022 15:25:16 -0300 Subject: [PATCH 091/107] feat: add info styled screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/clean.go | 2 +- cmd/info.go | 29 +++++++++++++------ .../reports/{report.go => command-summary.go} | 4 +-- 3 files changed, 23 insertions(+), 12 deletions(-) rename internal/reports/{report.go => command-summary.go} (95%) diff --git a/cmd/clean.go b/cmd/clean.go index 91850e547..e22b02a0f 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -79,7 +79,7 @@ re-create all Kubefirst files.`, cleanSummary.WriteString("Re-created empty config file: \n\n") cleanSummary.WriteString(fmt.Sprintf(" %q", config.KubefirstConfigFilePath)) - reports.CleanSummary(cleanSummary) + reports.CommandSummary(cleanSummary) }, } diff --git a/cmd/info.go b/cmd/info.go index c2467166c..d6e7de822 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -1,11 +1,14 @@ package cmd import ( + "bytes" "fmt" "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/reports" "github.com/spf13/cobra" "log" "runtime" + "strings" ) // infoCmd represents the info command @@ -17,15 +20,21 @@ var infoCmd = &cobra.Command{ config := configs.ReadConfig() - fmt.Printf("Kubefirst CLI version: v%s \n", config.KubefirstVersion) - fmt.Printf("Operational System: %s\n", config.LocalOs) - fmt.Printf("Architecture: %s\n", config.LocalArchitecture) - fmt.Printf("Go Lang version: v%s \n", runtime.Version()) - fmt.Printf("Kubefirst config file: %s\n", config.KubefirstConfigFilePath) - fmt.Printf("Kubefirst config folder: %s\n", config.K1FolderPath) - fmt.Printf("Kubectl path: %s\n", config.KubectlClientPath) - fmt.Printf("Terraform path: %s\n", config.TerraformPath) - fmt.Printf("Kubeconfig path: %s\n", config.KubeConfigPath) + var infoSummary bytes.Buffer + + infoSummary.WriteString(strings.Repeat("-", 70)) + infoSummary.WriteString("\nInfo summary:\n") + infoSummary.WriteString(strings.Repeat("-", 70)) + + //infoSummary.WriteString(fmt.Sprintf("Kubefirst CLI version: v%s \n", config.KubefirstVersion)) + infoSummary.WriteString(fmt.Sprintf("\n\nOperational System: %s\n", config.LocalOs)) + infoSummary.WriteString(fmt.Sprintf("Architecture: %s\n", config.LocalArchitecture)) + infoSummary.WriteString(fmt.Sprintf("Go Lang version: v%s \n", runtime.Version())) + infoSummary.WriteString(fmt.Sprintf("Kubefirst config file: %s\n", config.KubefirstConfigFilePath)) + infoSummary.WriteString(fmt.Sprintf("Kubefirst config folder: %s\n", config.K1FolderPath)) + infoSummary.WriteString(fmt.Sprintf("Kubectl path: %s\n", config.KubectlClientPath)) + infoSummary.WriteString(fmt.Sprintf("Terraform path: %s\n", config.TerraformPath)) + infoSummary.WriteString(fmt.Sprintf("Kubeconfig path: %s\n", config.KubeConfigPath)) err := configs.CheckKubefirstConfigFile(config) if err != nil { @@ -40,6 +49,8 @@ var infoCmd = &cobra.Command{ log.Panic(err) } fmt.Printf("----------- \n") + + reports.CommandSummary(infoSummary) }, } diff --git a/internal/reports/report.go b/internal/reports/command-summary.go similarity index 95% rename from internal/reports/report.go rename to internal/reports/command-summary.go index 2496a6ad7..8dfbd44db 100644 --- a/internal/reports/report.go +++ b/internal/reports/command-summary.go @@ -108,8 +108,8 @@ func max(a, b int) int { return b } -// CleanSummary receives a well-formatted buffer of bytes, and style it to the output. -func CleanSummary(cleanSummary bytes.Buffer) { +// CommandSummary receives a well-formatted buffer of bytes, and style it to the output. +func CommandSummary(cleanSummary bytes.Buffer) { const kubefirstBoldPurple = "#d0bae9" const kubefirstLightPurple = "#3c356c" From c9e3ee2b3a70aa8579575e9b6e8eb4c87eefa032 Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 14 Jul 2022 18:27:26 +0000 Subject: [PATCH 092/107] remove more noise Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/createUtils.go | 33 +++++++-------------------------- cmd/destroy.go | 26 ++++++++++++++++++-------- cmd/kubernetes.go | 12 +++--------- internal/argocd/argocd.go | 9 +++------ internal/gitlab/gitlab.go | 11 ++--------- internal/helm/helm.go | 18 ++++-------------- internal/k8s/kubernetes.go | 8 ++------ internal/vault/vault.go | 10 +++++++--- 8 files changed, 46 insertions(+), 81 deletions(-) diff --git a/cmd/createUtils.go b/cmd/createUtils.go index 06f96a69d..e2b8d8cc5 100644 --- a/cmd/createUtils.go +++ b/cmd/createUtils.go @@ -8,10 +8,9 @@ import ( "k8s.io/client-go/kubernetes" "github.com/kubefirst/kubefirst/internal/progressPrinter" "log" - "os" - "os/exec" "time" "fmt" + "github.com/kubefirst/kubefirst/pkg" ) @@ -70,10 +69,7 @@ func waitArgoCDToBeReady(dryRun bool){ config := configs.ReadConfig() x := 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/argocd") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() + _, _, 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) @@ -84,10 +80,7 @@ func waitArgoCDToBeReady(dryRun bool){ } } for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "pods", "-l", "app.kubernetes.io/name=argocd-server") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "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) @@ -107,10 +100,7 @@ func waitVaultToBeInitialized(dryRun bool) { config := configs.ReadConfig() x := 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/vault") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() + _, _, 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) @@ -122,10 +112,7 @@ func waitVaultToBeInitialized(dryRun bool) { } x = 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "get", "pods", "-l", "vault-initialized=true") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "get", "pods", "-l", "vault-initialized=true") if err != nil { log.Println("Waiting vault pods to create") time.Sleep(10 * time.Second) @@ -145,10 +132,7 @@ func waitGitlabToBeReady(dryRun bool) { config := configs.ReadConfig() x := 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/gitlab") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "get", "namespace/gitlab") if err != nil { log.Println("Waiting gitlab namespace to be born") time.Sleep(10 * time.Second) @@ -160,10 +144,7 @@ func waitGitlabToBeReady(dryRun bool) { } x = 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "get", "pods", "-l", "app=webservice") - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "get", "pods", "-l", "app=webservice") if err != nil { log.Println("Waiting gitlab pods to be born") time.Sleep(10 * time.Second) diff --git a/cmd/destroy.go b/cmd/destroy.go index 2e12de97c..397ef7507 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -8,9 +8,9 @@ import ( "github.com/kubefirst/kubefirst/internal/terraform" "github.com/spf13/cobra" "log" - "os" "os/exec" "syscall" + "bytes" ) // destroyCmd represents the destroy command @@ -42,30 +42,40 @@ if the registry has already been deleted.`, if err != nil { log.Panic(err) } - + + var kPortForwardOutb, kPortForwardErrb bytes.Buffer kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr + kPortForward.Stdout = &kPortForwardOutb + kPortForward.Stderr = &kPortForwardErrb defer kPortForward.Process.Signal(syscall.SIGTERM) err = kPortForward.Start() if err != nil { + log.Println("Commad Execution STDOUT: %s", kPortForwardOutb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardErrb.String()) log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) } + var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") - kPortForwardArgocd.Stdout = os.Stdout - kPortForwardArgocd.Stderr = os.Stderr + kPortForwardArgocd.Stdout = &kPortForwardArgocdOutb + kPortForwardArgocd.Stderr = &kPortForwardArgocdErrb err = kPortForwardArgocd.Start() defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) if err != nil { + log.Println("Commad Execution STDOUT: %s", kPortForwardArgocdOutb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardArgocdErrb.String()) log.Panicf("error: failed to port-forward to argocd in main thread %s", err) } + + var kPortForwardVaultOutb, kPortForwardVaultErrb bytes.Buffer kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") - kPortForwardVault.Stdout = os.Stdout - kPortForwardVault.Stderr = os.Stderr + kPortForwardVault.Stdout = &kPortForwardVaultOutb + kPortForwardVault.Stderr = &kPortForwardVaultErrb err = kPortForwardVault.Start() defer kPortForwardVault.Process.Signal(syscall.SIGTERM) if err != nil { + log.Println("Commad Execution STDOUT: %s", kPortForwardVaultOutb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardVaultErrb.String()) log.Panicf("error: failed to port-forward to vault in main thread %s", err) } log.Println("destroying gitlab terraform") diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index 95eac3d9d..9e973be5f 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -14,7 +14,7 @@ import ( "os" "os/exec" "time" - + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" @@ -144,10 +144,7 @@ func waitForNamespaceandPods(dryRun bool, config *configs.Config, namespace, pod if !viper.GetBool("create.softserve.ready") { x := 50 for i := 0; i < x; i++ { - kGetNamespace := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", namespace, "get", fmt.Sprintf("namespace/%s", namespace)) - kGetNamespace.Stdout = os.Stdout - kGetNamespace.Stderr = os.Stderr - err := kGetNamespace.Run() + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", namespace, "get", fmt.Sprintf("namespace/%s", namespace)) if err != nil { log.Println(fmt.Sprintf("waiting for %s namespace to create ", namespace)) time.Sleep(10 * time.Second) @@ -158,10 +155,7 @@ func waitForNamespaceandPods(dryRun bool, config *configs.Config, namespace, pod } } for i := 0; i < x; i++ { - kGetPods := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", namespace, "get", "pods", "-l", podLabel) - kGetPods.Stdout = os.Stdout - kGetPods.Stderr = os.Stderr - err := kGetPods.Run() + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", namespace, "get", "pods", "-l", podLabel) if err != nil { log.Println(fmt.Sprintf("waiting for %s pods to create ", namespace)) time.Sleep(10 * time.Second) diff --git a/internal/argocd/argocd.go b/internal/argocd/argocd.go index 82a07bb56..d852f5c0a 100644 --- a/internal/argocd/argocd.go +++ b/internal/argocd/argocd.go @@ -10,10 +10,10 @@ import ( "io/ioutil" "log" "net/http" - "os" - "os/exec" + "strings" "time" + "github.com/kubefirst/kubefirst/pkg" ) // kSyncArgocdApplication request ArgoCD to manual sync an application. Expected parameters are the ArgoCD application @@ -182,10 +182,7 @@ func SyncArgocdApplication(dryRun bool, applicationName, argocdAuthToken string) url := fmt.Sprintf("https://localhost:8080/api/v1/applications/%s/sync", applicationName) var outb bytes.Buffer - argoCdAppSync := exec.Command("curl", "-k", "-L", "-X", "POST", url, "-H", fmt.Sprintf("Authorization: Bearer %s", argocdAuthToken)) - argoCdAppSync.Stdout = os.Stdout - argoCdAppSync.Stderr = os.Stderr - err := argoCdAppSync.Run() + _, _, err := pkg.ExecShellReturnStrings("curl", "-k", "-L", "-X", "POST", url, "-H", fmt.Sprintf("Authorization: Bearer %s", argocdAuthToken)) log.Println("the value from the curl command to sync registry in argocd is:", outb.String()) if err != nil { log.Panicf("error: curl appSync failed failed %s", err) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index c1468290d..0c097610a 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -29,7 +29,6 @@ import ( "net/http" "net/url" "os" - "os/exec" "strings" "time" @@ -69,10 +68,7 @@ func GitlabGeneratePersonalAccessToken(gitlabPodName string) { id := uuid.New() gitlabToken := id.String()[:20] - k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err := k.Run() + _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "exec", gitlabPodName, "--", "gitlab-rails", "runner", fmt.Sprintf("token = User.find_by_username('root').personal_access_tokens.create(scopes: [:write_registry, :write_repository, :api], name: 'Automation token'); token.set_token('%s'); token.save!", gitlabToken)) if err != nil { log.Panicf("error running exec against %s to generate gitlab personal access token for root user", gitlabPodName) } @@ -452,10 +448,7 @@ func ChangeRegistryToGitLab(dryRun bool) { log.Panicf("error creating argocd repository connection secret %s", err) } - k := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.K1srtFolderPath)) - k.Stdout = os.Stdout - k.Stderr = os.Stderr - err = k.Run() + _, _, err = pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.K1srtFolderPath)) if err != nil { log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) } diff --git a/internal/helm/helm.go b/internal/helm/helm.go index 9754a972a..616692f90 100644 --- a/internal/helm/helm.go +++ b/internal/helm/helm.go @@ -3,10 +3,9 @@ package helm import ( "fmt" "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" "log" - "os" - "os/exec" ) func InstallArgocd(dryRun bool) { @@ -17,26 +16,17 @@ func InstallArgocd(dryRun bool) { return } // ! commenting out until a clean execution is necessary // create namespace - helmRepoAddArgocd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") - helmRepoAddArgocd.Stdout = os.Stdout - helmRepoAddArgocd.Stderr = os.Stderr - err := helmRepoAddArgocd.Run() + _, _, err := pkg.ExecShellReturnStrings(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "add", "argo", "https://argoproj.github.io/argo-helm") if err != nil { log.Panicf("error: could not run helm repo add %s", err) } - helmRepoUpdate := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "update") - helmRepoUpdate.Stdout = os.Stdout - helmRepoUpdate.Stderr = os.Stderr - err = helmRepoUpdate.Run() + _, _, err = pkg.ExecShellReturnStrings(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "repo", "update") if err != nil { log.Panicf("error: could not helm repo update %s", err) } - helmInstallArgocdCmd := exec.Command(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/argocd-init-values.yaml", config.K1srtFolderPath), "argo/argo-cd") - helmInstallArgocdCmd.Stdout = os.Stdout - helmInstallArgocdCmd.Stderr = os.Stderr - err = helmInstallArgocdCmd.Run() + _, _, err = pkg.ExecShellReturnStrings(config.HelmClientPath, "--kubeconfig", config.KubeConfigPath, "upgrade", "--install", "argocd", "--namespace", "argocd", "--create-namespace", "--wait", "--values", fmt.Sprintf("%s/argocd-init-values.yaml", config.K1srtFolderPath), "argo/argo-cd") if err != nil { log.Panicf("error: could not helm install argocd command %s", err) } diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index 9f4ffeaa0..877404718 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -12,9 +12,8 @@ import ( "github.com/spf13/viper" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" + "github.com/kubefirst/kubefirst/pkg" "log" - "os" - "os/exec" "time" ) @@ -74,10 +73,7 @@ func DeleteRegistryApplication(skipDeleteRegistryApplication bool) { argocd.GetArgocdAuthToken(false) url := "https://localhost:8080/api/v1/applications/registry" - argoCdAppSync := exec.Command("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) - argoCdAppSync.Stdout = os.Stdout - argoCdAppSync.Stderr = os.Stderr - err := argoCdAppSync.Run() + _, _, err := pkg.ExecShellReturnStrings("curl", "-k", "-vL", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", viper.GetString("argocd.admin.apitoken"))) if err != nil { log.Panicf("error: delete registry applicatoin from argocd failed: %s", err) } diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 3853b75b2..ae8a26821 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -18,6 +18,7 @@ import ( "os" "os/exec" "syscall" + "bytes" ) // GetVaultRootToken get `vault-unseal-keys` token on Vault. @@ -77,13 +78,16 @@ func ConfigureVault(dryRun bool) { viper.Set("vault.token", vaultToken) viper.WriteConfig() - + + var kPortForwardOutb, kPortForwardErrb bytes.Buffer kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") - kPortForward.Stdout = os.Stdout - kPortForward.Stderr = os.Stderr + kPortForward.Stdout = &kPortForwardOutb + kPortForward.Stderr = &kPortForwardErrb err = kPortForward.Start() defer kPortForward.Process.Signal(syscall.SIGTERM) if err != nil { + log.Println("Commad Execution STDOUT: %s", kPortForwardOutb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardErrb.String()) log.Panicf("error: failed to port-forward to vault namespce svc/vault %s", err) } From 60b24f8007906fcdeff9dab55ef1d17f5d8004dc Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 14 Jul 2022 18:40:32 +0000 Subject: [PATCH 093/107] fix vars Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- internal/gitlab/gitlab.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index f36b65647..c4cb695d3 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -336,7 +336,7 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { log.Panicf("error: could not change directory to " + directory) } - os.Setenv("GITLAB_BASE_URL", viper.GetString("gitlab.local.service")) + envs["GITLAB_BASE_URL"] = viper.GetString("gitlab.local.service") if !skipGitlabTerraform { err = pkg.ExecShellWithVars(envs, config.TerraformPath, "init") From 50e22b400fb9c5a8dbad8f34ea942489653e843d Mon Sep 17 00:00:00 2001 From: 6za <53096417+6za@users.noreply.github.com> Date: Thu, 14 Jul 2022 18:56:14 +0000 Subject: [PATCH 094/107] fix lint stuff Signed-off-by: 6za <53096417+6za@users.noreply.github.com> --- cmd/createUtils.go | 42 ++++++++++++++++++-------------------- cmd/destroy.go | 10 ++++----- cmd/kubernetes.go | 22 ++++++++++---------- internal/argocd/argocd.go | 2 +- internal/k8s/kubernetes.go | 2 +- internal/vault/vault.go | 8 ++++---- 6 files changed, 42 insertions(+), 44 deletions(-) diff --git a/cmd/createUtils.go b/cmd/createUtils.go index e2b8d8cc5..1411fb6b0 100644 --- a/cmd/createUtils.go +++ b/cmd/createUtils.go @@ -1,19 +1,17 @@ package cmd import ( + "fmt" + "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/internal/progressPrinter" "github.com/kubefirst/kubefirst/internal/telemetry" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" - "github.com/kubefirst/kubefirst/configs" - "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/kubernetes" - "github.com/kubefirst/kubefirst/internal/progressPrinter" + "k8s.io/client-go/tools/clientcmd" "log" "time" - "fmt" - "github.com/kubefirst/kubefirst/pkg" -) - - +) // todo: move it to internals/ArgoCD func setArgocdCreds(dryRun bool) { @@ -22,8 +20,8 @@ func setArgocdCreds(dryRun bool) { viper.Set("argocd.admin.password", "dry-run-not-real-pwd") viper.Set("argocd.admin.username", "dry-run-not-admin") viper.WriteConfig() - return - } + return + } cfg := configs.ReadConfig() config, err := clientcmd.BuildConfigFromFlags("", cfg.KubeConfigPath) @@ -43,16 +41,16 @@ func setArgocdCreds(dryRun bool) { viper.WriteConfig() } -func sendStartedInstallTelemetry(dryRun bool){ +func sendStartedInstallTelemetry(dryRun bool) { metricName := "kubefirst.mgmt_cluster_install.started" if !dryRun { - telemetry.SendTelemetry( viper.GetString("aws.hostedzonename"), metricName) + telemetry.SendTelemetry(viper.GetString("aws.hostedzonename"), metricName) } else { log.Printf("[#99] Dry-run mode, telemetry skipped: %s", metricName) } } -func sendCompleteInstallTelemetry(dryRun bool){ +func sendCompleteInstallTelemetry(dryRun bool) { metricName := "kubefirst.mgmt_cluster_install.completed" if !dryRun { telemetry.SendTelemetry(viper.GetString("aws.hostedzonename"), metricName) @@ -61,11 +59,11 @@ func sendCompleteInstallTelemetry(dryRun bool){ } } -func waitArgoCDToBeReady(dryRun bool){ +func waitArgoCDToBeReady(dryRun bool) { if dryRun { log.Printf("[#99] Dry-run mode, waitArgoCDToBeReady skipped.") - return - } + return + } config := configs.ReadConfig() x := 50 for i := 0; i < x; i++ { @@ -95,8 +93,8 @@ func waitArgoCDToBeReady(dryRun bool){ func waitVaultToBeInitialized(dryRun bool) { if dryRun { log.Printf("[#99] Dry-run mode, waitVaultToBeInitialized skipped.") - return - } + return + } config := configs.ReadConfig() x := 50 for i := 0; i < x; i++ { @@ -127,8 +125,8 @@ func waitVaultToBeInitialized(dryRun bool) { func waitGitlabToBeReady(dryRun bool) { if dryRun { log.Printf("[#99] Dry-run mode, waitVaultToBeInitialized skipped.") - return - } + return + } config := configs.ReadConfig() x := 50 for i := 0; i < x; i++ { @@ -158,7 +156,7 @@ func waitGitlabToBeReady(dryRun bool) { } //Notify user in the STOUT and also logfile -func informUser(message string){ +func informUser(message string) { log.Println(message) - progressPrinter.LogMessage(fmt.Sprintf("- %s",message)) + progressPrinter.LogMessage(fmt.Sprintf("- %s", message)) } diff --git a/cmd/destroy.go b/cmd/destroy.go index 397ef7507..f657a6776 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -1,6 +1,7 @@ package cmd import ( + "bytes" "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/internal/aws" "github.com/kubefirst/kubefirst/internal/gitlab" @@ -10,7 +11,6 @@ import ( "log" "os/exec" "syscall" - "bytes" ) // destroyCmd represents the destroy command @@ -42,8 +42,8 @@ if the registry has already been deleted.`, if err != nil { log.Panic(err) } - - var kPortForwardOutb, kPortForwardErrb bytes.Buffer + + var kPortForwardOutb, kPortForwardErrb bytes.Buffer kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") kPortForward.Stdout = &kPortForwardOutb kPortForward.Stderr = &kPortForwardErrb @@ -55,7 +55,7 @@ if the registry has already been deleted.`, log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) } - var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer + var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") kPortForwardArgocd.Stdout = &kPortForwardArgocdOutb kPortForwardArgocd.Stderr = &kPortForwardArgocdErrb @@ -67,7 +67,7 @@ if the registry has already been deleted.`, log.Panicf("error: failed to port-forward to argocd in main thread %s", err) } - var kPortForwardVaultOutb, kPortForwardVaultErrb bytes.Buffer + var kPortForwardVaultOutb, kPortForwardVaultErrb bytes.Buffer kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") kPortForwardVault.Stdout = &kPortForwardVaultOutb kPortForwardVault.Stderr = &kPortForwardVaultErrb diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index 9e973be5f..0a55ac04b 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -10,14 +10,14 @@ import ( "encoding/json" "fmt" "github.com/kubefirst/kubefirst/configs" - "log" - "os" - "os/exec" - "time" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" + "log" + "os" + "os/exec" + "time" ) var vaultRootToken string @@ -43,7 +43,7 @@ func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) func waitForVaultUnseal(dryRun bool, config *configs.Config) { if dryRun { log.Printf("[#99] Dry-run mode, waitForGitlab skipped.") - return + return } vaultReady := viper.GetBool("create.vault.ready") if !vaultReady { @@ -67,8 +67,8 @@ func waitForVaultUnseal(dryRun bool, config *configs.Config) { func waitForGitlab(dryRun bool, config *configs.Config) { if dryRun { log.Printf("[#99] Dry-run mode, waitForGitlab skipped.") - return - } + return + } var output bytes.Buffer // todo - add a viper.GetBool() check to the beginning of this function // todo write in golang? see here -> https://github.com/bcreane/k8sutils/blob/master/utils.go @@ -85,8 +85,8 @@ func waitForGitlab(dryRun bool, config *configs.Config) { func createVaultConfiguredSecret(dryRun bool, config *configs.Config) { if dryRun { log.Printf("[#99] Dry-run mode, createVaultConfiguredSecret skipped.") - return - } + return + } if !viper.GetBool("vault.configuredsecret") { var output bytes.Buffer // todo - https://github.com/bcreane/k8sutils/blob/master/utils.go @@ -137,10 +137,10 @@ func getSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key strin } func waitForNamespaceandPods(dryRun bool, config *configs.Config, namespace, podLabel string) { - if dryRun { + if dryRun { log.Printf("[#99] Dry-run mode, waitForNamespaceandPods skipped") return - } + } if !viper.GetBool("create.softserve.ready") { x := 50 for i := 0; i < x; i++ { diff --git a/internal/argocd/argocd.go b/internal/argocd/argocd.go index d852f5c0a..c940b8c81 100644 --- a/internal/argocd/argocd.go +++ b/internal/argocd/argocd.go @@ -11,9 +11,9 @@ import ( "log" "net/http" + "github.com/kubefirst/kubefirst/pkg" "strings" "time" - "github.com/kubefirst/kubefirst/pkg" ) // kSyncArgocdApplication request ArgoCD to manual sync an application. Expected parameters are the ArgoCD application diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index 877404718..f3f5c77b9 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -9,10 +9,10 @@ import ( "encoding/json" "fmt" "github.com/kubefirst/kubefirst/internal/argocd" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" - "github.com/kubefirst/kubefirst/pkg" "log" "time" ) diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 711c2ad7a..b96345fe8 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -1,6 +1,7 @@ package vault import ( + "bytes" "context" "encoding/json" "fmt" @@ -18,7 +19,6 @@ import ( "os" "os/exec" "syscall" - "bytes" ) // GetVaultRootToken get `vault-unseal-keys` token on Vault. @@ -78,8 +78,8 @@ func ConfigureVault(dryRun bool) { viper.Set("vault.token", vaultToken) viper.WriteConfig() - - var kPortForwardOutb, kPortForwardErrb bytes.Buffer + + var kPortForwardOutb, kPortForwardErrb bytes.Buffer kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") kPortForward.Stdout = &kPortForwardOutb kPortForward.Stderr = &kPortForwardErrb @@ -87,7 +87,7 @@ func ConfigureVault(dryRun bool) { defer kPortForward.Process.Signal(syscall.SIGTERM) if err != nil { log.Println("Commad Execution STDOUT: %s", kPortForwardOutb.String()) - log.Println("Commad Execution STDERR: %s", kPortForwardErrb.String()) + log.Println("Commad Execution STDERR: %s", kPortForwardErrb.String()) log.Panicf("error: failed to port-forward to vault namespce svc/vault %s", err) } From 7989ee4fc2d3a69996359451b72e13bb6ffbf6c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Thu, 14 Jul 2022 17:47:20 -0300 Subject: [PATCH 095/107] feat: add handoff for create command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 60 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 20138f6b6..d7cbe37b6 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -8,6 +8,7 @@ import ( "github.com/kubefirst/kubefirst/internal/gitlab" "github.com/kubefirst/kubefirst/internal/helm" "github.com/kubefirst/kubefirst/internal/progressPrinter" + "github.com/kubefirst/kubefirst/internal/reports" "github.com/kubefirst/kubefirst/internal/softserve" "github.com/kubefirst/kubefirst/internal/terraform" "github.com/kubefirst/kubefirst/internal/vault" @@ -15,15 +16,11 @@ import ( "github.com/spf13/viper" "log" "os/exec" + "strings" "syscall" "time" ) -const trackerStage20 = "0 - Apply Base" -const trackerStage21 = "1 - Temporary SCM Install" -const trackerStage22 = "2 - Argo/Final SCM Install" -const trackerStage23 = "3 - Final Setup" - // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", @@ -52,7 +49,57 @@ to quickly create a Cobra application.`, log.Panic(err) } - infoCmd.Run(cmd, args) + // todo: remove it? + //infoCmd.Run(cmd, args) + + var handOffData bytes.Buffer + type createHandOff struct { + AwsAccountId string + AwsHostedZoneName string + AwsRegion string + ClusterName string + ArgoCDUrl string + ArgoCDUsername string + ArgoCDPassword string + } + + clusterData := createHandOff{ + ClusterName: viper.GetString("cluster-name"), + AwsAccountId: viper.GetString("aws.accountid"), + AwsHostedZoneName: viper.GetString("aws.hostedzonename"), + AwsRegion: viper.GetString("aws.region"), + ArgoCDUrl: viper.GetString("argocd.local.service"), + ArgoCDUsername: viper.GetString("argocd.admin.username"), + ArgoCDPassword: viper.GetString("argocd.admin.password"), + } + + handOffData.WriteString(strings.Repeat("-", 70)) + handOffData.WriteString(fmt.Sprintf("\nCluster %q is up and running!:\n", clusterData.ClusterName)) + handOffData.WriteString(strings.Repeat("-", 70)) + + // todo: make it dynamic for GCP + handOffData.WriteString("\n\n--- AWS ") + handOffData.WriteString(strings.Repeat("-", 62)) + handOffData.WriteString(fmt.Sprintf("\n AWS Account Id: %s\n", clusterData.AwsAccountId)) + handOffData.WriteString(fmt.Sprintf(" AWS hosted zone name: %s\n", clusterData.AwsHostedZoneName)) + handOffData.WriteString(fmt.Sprintf(" AWS region: %s\n", clusterData.AwsRegion)) + handOffData.WriteString(strings.Repeat("-", 70)) + + handOffData.WriteString("\n\n--- ArgoCD ") + handOffData.WriteString(strings.Repeat("-", 59)) + handOffData.WriteString(fmt.Sprintf("\n URL: %s\n", clusterData.ArgoCDUrl)) + handOffData.WriteString(fmt.Sprintf(" username: %s\n", clusterData.ArgoCDUsername)) + handOffData.WriteString(fmt.Sprintf(" password: %s\n", clusterData.ArgoCDPassword)) + handOffData.WriteString(strings.Repeat("-", 70)) + + reports.CommandSummary(handOffData) + + if dryRun { + fmt.Println("dry running...") + log.Println("020202020") + return + } + progressPrinter.IncrementTracker("step-0", 1) progressPrinter.AddTracker("step-softserve", "Prepare Temporary Repo ", 4) @@ -259,6 +306,7 @@ to quickly create a Cobra application.`, } sendCompleteInstallTelemetry(dryRun) time.Sleep(time.Millisecond * 100) + }, } From 74ad9903b2258eb7103feb59d888e341d1b8f958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Fri, 15 Jul 2022 09:35:45 -0300 Subject: [PATCH 096/107] feat: add non styled info command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/info.go | 4 ++-- internal/reports/style.go | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 internal/reports/style.go diff --git a/cmd/info.go b/cmd/info.go index d6e7de822..443954e6c 100755 --- a/cmd/info.go +++ b/cmd/info.go @@ -49,8 +49,8 @@ var infoCmd = &cobra.Command{ log.Panic(err) } fmt.Printf("----------- \n") - - reports.CommandSummary(infoSummary) + + fmt.Println(reports.StyleMessage(infoSummary.String())) }, } diff --git a/internal/reports/style.go b/internal/reports/style.go new file mode 100644 index 000000000..2f54d64e4 --- /dev/null +++ b/internal/reports/style.go @@ -0,0 +1,23 @@ +package reports + +import ( + "github.com/charmbracelet/lipgloss" +) + +// StyleMessage receives a string and return a style string +func StyleMessage(message string) string { + + const kubefirstBoldPurple = "#d0bae9" + const kubefirstLightPurple = "#3c356c" + + var style = lipgloss.NewStyle(). + Foreground(lipgloss.Color(kubefirstBoldPurple)). + Background(lipgloss.Color(kubefirstLightPurple)). + PaddingTop(2). + PaddingBottom(2). + PaddingLeft(2). + PaddingRight(2). + Width(75) + + return style.Render(message) +} From 0483782d8ca73c405bdb8aa45285a5f9f00b4e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Fri, 15 Jul 2022 11:16:53 -0300 Subject: [PATCH 097/107] chore: clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- internal/reports/command-summary.go | 26 +++++++++++++++++------- internal/reports/command-summary_test.go | 20 ++++++++++++++++++ internal/reports/style.go | 23 --------------------- 3 files changed, 39 insertions(+), 30 deletions(-) create mode 100644 internal/reports/command-summary_test.go delete mode 100644 internal/reports/style.go diff --git a/internal/reports/command-summary.go b/internal/reports/command-summary.go index 8dfbd44db..50b9ca935 100644 --- a/internal/reports/command-summary.go +++ b/internal/reports/command-summary.go @@ -111,6 +111,19 @@ func max(a, b int) int { // CommandSummary receives a well-formatted buffer of bytes, and style it to the output. func CommandSummary(cleanSummary bytes.Buffer) { + style := getStyle() + + p := tea.NewProgram( + Model{Content: style.Render(cleanSummary.String())}, + ) + + if err := p.Start(); err != nil { + log.Panicf("unable to load reports screen, error is: %s", err) + } +} + +func getStyle() lipgloss.Style { + const kubefirstBoldPurple = "#d0bae9" const kubefirstLightPurple = "#3c356c" @@ -122,12 +135,11 @@ func CommandSummary(cleanSummary bytes.Buffer) { PaddingLeft(2). PaddingRight(2). Width(75) + return style +} - p := tea.NewProgram( - Model{Content: style.Render(cleanSummary.String())}, - ) - - if err := p.Start(); err != nil { - log.Panicf("unable to load reports screen, error is: %s", err) - } +// StyleMessage receives a string and return a style string +func StyleMessage(message string) string { + style := getStyle() + return style.Render(message) } diff --git a/internal/reports/command-summary_test.go b/internal/reports/command-summary_test.go new file mode 100644 index 000000000..685aea544 --- /dev/null +++ b/internal/reports/command-summary_test.go @@ -0,0 +1,20 @@ +package reports + +import ( + "strings" + "testing" +) + +// TestStyleMessage test if a regular string get transformed into a new styled string and keep its original content +// not changed +func TestStyleMessage(t *testing.T) { + + message := "kubefirst rock!" + got := StyleMessage(message) + wanted := strings.Contains(got, message) + + if wanted != true { + t.Error("returned string doesnt contain the content of the initial message") + } + +} diff --git a/internal/reports/style.go b/internal/reports/style.go deleted file mode 100644 index 2f54d64e4..000000000 --- a/internal/reports/style.go +++ /dev/null @@ -1,23 +0,0 @@ -package reports - -import ( - "github.com/charmbracelet/lipgloss" -) - -// StyleMessage receives a string and return a style string -func StyleMessage(message string) string { - - const kubefirstBoldPurple = "#d0bae9" - const kubefirstLightPurple = "#3c356c" - - var style = lipgloss.NewStyle(). - Foreground(lipgloss.Color(kubefirstBoldPurple)). - Background(lipgloss.Color(kubefirstLightPurple)). - PaddingTop(2). - PaddingBottom(2). - PaddingLeft(2). - PaddingRight(2). - Width(75) - - return style.Render(message) -} From 6397725827ab66e8e0298c0759ae59ab70c21620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Fri, 15 Jul 2022 12:33:57 -0300 Subject: [PATCH 098/107] chore: clean up, and add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/create.go | 79 ++++++++++++--------------------- internal/reports/create.go | 50 +++++++++++++++++++++ internal/reports/create_test.go | 29 ++++++++++++ 3 files changed, 107 insertions(+), 51 deletions(-) create mode 100644 internal/reports/create.go create mode 100644 internal/reports/create_test.go diff --git a/cmd/create.go b/cmd/create.go index d7cbe37b6..3a0de9e7a 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -16,7 +16,6 @@ import ( "github.com/spf13/viper" "log" "os/exec" - "strings" "syscall" "time" ) @@ -49,56 +48,10 @@ to quickly create a Cobra application.`, log.Panic(err) } - // todo: remove it? - //infoCmd.Run(cmd, args) - - var handOffData bytes.Buffer - type createHandOff struct { - AwsAccountId string - AwsHostedZoneName string - AwsRegion string - ClusterName string - ArgoCDUrl string - ArgoCDUsername string - ArgoCDPassword string - } - - clusterData := createHandOff{ - ClusterName: viper.GetString("cluster-name"), - AwsAccountId: viper.GetString("aws.accountid"), - AwsHostedZoneName: viper.GetString("aws.hostedzonename"), - AwsRegion: viper.GetString("aws.region"), - ArgoCDUrl: viper.GetString("argocd.local.service"), - ArgoCDUsername: viper.GetString("argocd.admin.username"), - ArgoCDPassword: viper.GetString("argocd.admin.password"), - } - - handOffData.WriteString(strings.Repeat("-", 70)) - handOffData.WriteString(fmt.Sprintf("\nCluster %q is up and running!:\n", clusterData.ClusterName)) - handOffData.WriteString(strings.Repeat("-", 70)) - - // todo: make it dynamic for GCP - handOffData.WriteString("\n\n--- AWS ") - handOffData.WriteString(strings.Repeat("-", 62)) - handOffData.WriteString(fmt.Sprintf("\n AWS Account Id: %s\n", clusterData.AwsAccountId)) - handOffData.WriteString(fmt.Sprintf(" AWS hosted zone name: %s\n", clusterData.AwsHostedZoneName)) - handOffData.WriteString(fmt.Sprintf(" AWS region: %s\n", clusterData.AwsRegion)) - handOffData.WriteString(strings.Repeat("-", 70)) - - handOffData.WriteString("\n\n--- ArgoCD ") - handOffData.WriteString(strings.Repeat("-", 59)) - handOffData.WriteString(fmt.Sprintf("\n URL: %s\n", clusterData.ArgoCDUrl)) - handOffData.WriteString(fmt.Sprintf(" username: %s\n", clusterData.ArgoCDUsername)) - handOffData.WriteString(fmt.Sprintf(" password: %s\n", clusterData.ArgoCDPassword)) - handOffData.WriteString(strings.Repeat("-", 70)) - - reports.CommandSummary(handOffData) - - if dryRun { - fmt.Println("dry running...") - log.Println("020202020") - return - } + // todo: + // isolate commands, in case we want to run some validations on the create, it would be a good idea to call the + // functions that does the validations + infoCmd.Run(cmd, args) progressPrinter.IncrementTracker("step-0", 1) @@ -307,6 +260,30 @@ to quickly create a Cobra application.`, sendCompleteInstallTelemetry(dryRun) time.Sleep(time.Millisecond * 100) + if dryRun { + log.Println("no handoff data on dry-run mode") + return + } + + // prepare data for the handoff report + clusterData := reports.CreateHandOff{ + ClusterName: viper.GetString("cluster-name"), + AwsAccountId: viper.GetString("aws.accountid"), + AwsHostedZoneName: viper.GetString("aws.hostedzonename"), + AwsRegion: viper.GetString("aws.region"), + ArgoCDUrl: viper.GetString("argocd.local.service"), + ArgoCDUsername: viper.GetString("argocd.admin.username"), + ArgoCDPassword: viper.GetString("argocd.admin.password"), + VaultUrl: viper.GetString("vault.local.service"), + VaultToken: viper.GetString("vault.token"), + } + + // build the string that will be sent to the report + handOffData := reports.BuildCreateHandOffReport(clusterData) + + // call handoff report and apply style + reports.CommandSummary(handOffData) + }, } diff --git a/internal/reports/create.go b/internal/reports/create.go new file mode 100644 index 000000000..ee11e42c2 --- /dev/null +++ b/internal/reports/create.go @@ -0,0 +1,50 @@ +package reports + +import ( + "bytes" + "fmt" + "strings" +) + +type CreateHandOff struct { + AwsAccountId string + AwsHostedZoneName string + AwsRegion string + ClusterName string + ArgoCDUrl string + ArgoCDUsername string + ArgoCDPassword string + VaultUrl string + VaultToken string +} + +func BuildCreateHandOffReport(clusterData CreateHandOff) bytes.Buffer { + + var handOffData bytes.Buffer + handOffData.WriteString(strings.Repeat("-", 70)) + handOffData.WriteString(fmt.Sprintf("\nCluster %q is up and running!:\n", clusterData.ClusterName)) + handOffData.WriteString(strings.Repeat("-", 70)) + + handOffData.WriteString("\n\n--- AWS ") + handOffData.WriteString(strings.Repeat("-", 62)) + handOffData.WriteString(fmt.Sprintf("\n AWS Account Id: %s\n", clusterData.AwsAccountId)) + handOffData.WriteString(fmt.Sprintf(" AWS hosted zone name: %s\n", clusterData.AwsHostedZoneName)) + handOffData.WriteString(fmt.Sprintf(" AWS region: %s\n", clusterData.AwsRegion)) + handOffData.WriteString(strings.Repeat("-", 70)) + + handOffData.WriteString("\n\n--- ArgoCD ") + handOffData.WriteString(strings.Repeat("-", 59)) + handOffData.WriteString(fmt.Sprintf("\n URL: %s\n", clusterData.ArgoCDUrl)) + handOffData.WriteString(fmt.Sprintf(" username: %s\n", clusterData.ArgoCDUsername)) + handOffData.WriteString(fmt.Sprintf(" password: %s\n", clusterData.ArgoCDPassword)) + handOffData.WriteString(strings.Repeat("-", 70)) + + handOffData.WriteString("\n\n--- Vault ") + handOffData.WriteString(strings.Repeat("-", 59)) + handOffData.WriteString(fmt.Sprintf("\n URL: %s\n", clusterData.VaultUrl)) + handOffData.WriteString(fmt.Sprintf(" token: %s\n", clusterData.VaultToken)) + handOffData.WriteString(strings.Repeat("-", 70)) + + return handOffData + +} diff --git a/internal/reports/create_test.go b/internal/reports/create_test.go new file mode 100644 index 000000000..3616fd90e --- /dev/null +++ b/internal/reports/create_test.go @@ -0,0 +1,29 @@ +package reports + +import ( + "strings" + "testing" +) + +// todo: test all conditions using table test table +// TestBuildCreateHandOffReport test if the built string is consuming the required data +func TestBuildCreateHandOffReport(t *testing.T) { + mockURL := "mock-url" + mockUsername := "mock-username" + mockPassword := "mock-password" + + handOffData := CreateHandOff{ + ArgoCDUrl: mockURL, + ArgoCDUsername: mockUsername, + ArgoCDPassword: mockPassword, + } + got := BuildCreateHandOffReport(handOffData) + + if !strings.Contains(got.String(), mockUsername) { + t.Errorf("built buffer doesn't contain %q", mockUsername) + } + + if !strings.Contains(got.String(), mockPassword) { + t.Errorf("built buffer doesn't contain %q", mockPassword) + } +} From 30eff08c76d2d82cd2e2c32ffceb0ac64c1454c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Fri, 15 Jul 2022 13:12:40 -0300 Subject: [PATCH 099/107] chore: enable docker colors, use 256 colors to avoid colors mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- docker-compose.yaml | 2 ++ internal/reports/command-summary.go | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 8070ea994..55ea902e3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,6 +7,8 @@ services: build: context: ./build container_name: kubefirst-dev + environment: + TERM: xterm-256color ports: - "8080:8080" # ArgoCD - "8888:8888" # GitLab diff --git a/internal/reports/command-summary.go b/internal/reports/command-summary.go index 50b9ca935..3b2168491 100644 --- a/internal/reports/command-summary.go +++ b/internal/reports/command-summary.go @@ -124,12 +124,12 @@ func CommandSummary(cleanSummary bytes.Buffer) { func getStyle() lipgloss.Style { - const kubefirstBoldPurple = "#d0bae9" - const kubefirstLightPurple = "#3c356c" + const kubefirstBoldPurple = "#5f00af" + const kubefirstLightPurple = "#d0bae9" var style = lipgloss.NewStyle(). - Foreground(lipgloss.Color(kubefirstBoldPurple)). - Background(lipgloss.Color(kubefirstLightPurple)). + Foreground(lipgloss.Color(kubefirstLightPurple)). + Background(lipgloss.Color(kubefirstBoldPurple)). PaddingTop(2). PaddingBottom(2). PaddingLeft(2). From f5298181fd564dd92df291c657adade174768936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vanzuita?= Date: Fri, 15 Jul 2022 14:30:59 -0300 Subject: [PATCH 100/107] chore: update info screen to be non blocking screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Vanzuita --- cmd/clean.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/clean.go b/cmd/clean.go index e22b02a0f..70070ed80 100644 --- a/cmd/clean.go +++ b/cmd/clean.go @@ -79,7 +79,7 @@ re-create all Kubefirst files.`, cleanSummary.WriteString("Re-created empty config file: \n\n") cleanSummary.WriteString(fmt.Sprintf(" %q", config.KubefirstConfigFilePath)) - reports.CommandSummary(cleanSummary) + fmt.Println(reports.StyleMessage(cleanSummary.String())) }, } From 25a6a5a722185e655140c50469eb9a557a5900f9 Mon Sep 17 00:00:00 2001 From: John Dietz Date: Fri, 15 Jul 2022 13:55:03 -0400 Subject: [PATCH 101/107] static kubeconfig naming convention (#112) Signed-off-by: johndietz --- cmd/kubefirstTemplate.go | 16 ++++++++++------ configs/config.go | 5 +++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 53e012ef8..9bde96af1 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -2,18 +2,19 @@ package cmd import ( "fmt" - "github.com/go-git/go-git/v5" - gitConfig "github.com/go-git/go-git/v5/config" - "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/object" - "github.com/kubefirst/kubefirst/configs" - "github.com/spf13/viper" "io/ioutil" "log" "os" "path/filepath" "strings" "time" + + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" + "github.com/kubefirst/kubefirst/configs" + "github.com/spf13/viper" ) func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName string, branch string) { @@ -127,6 +128,8 @@ func detokenizeDirectory(path string, fi os.FileInfo, err error) error { bucketArgoArtifacts := viper.GetString("bucket.argo-artifacts.name") bucketGitlabBackup := viper.GetString("bucket.gitlab-backup.name") bucketChartmuseum := viper.GetString("bucket.chartmuseum.name") + clusterName := viper.GetString("cluster-name") + region := viper.GetString("aws.region") adminEmail := viper.GetString("adminemail") awsAccountId := viper.GetString("aws.accountid") @@ -142,6 +145,7 @@ func detokenizeDirectory(path string, fi os.FileInfo, err error) error { newContents = strings.Replace(newContents, "", region, -1) newContents = strings.Replace(newContents, "", adminEmail, -1) newContents = strings.Replace(newContents, "", awsAccountId, -1) + newContents = strings.Replace(newContents, "", clusterName, -1) if kmsKeyId != "" { newContents = strings.Replace(newContents, "", kmsKeyId, -1) } diff --git a/configs/config.go b/configs/config.go index 89e47fdac..517f26a8a 100644 --- a/configs/config.go +++ b/configs/config.go @@ -2,10 +2,11 @@ package configs import ( "fmt" - "github.com/caarlos0/env/v6" "log" "os" "runtime" + + "github.com/caarlos0/env/v6" ) /** @@ -61,7 +62,7 @@ func ReadConfig() *Config { config.LocalArchitecture = runtime.GOARCH config.KubectlClientPath = fmt.Sprintf("%s/tools/kubectl", config.K1FolderPath) - config.KubeConfigPath = fmt.Sprintf("%s/gitops/terraform/base/kubeconfig_kubefirst", config.K1FolderPath) + config.KubeConfigPath = fmt.Sprintf("%s/gitops/terraform/base/kubeconfig", config.K1FolderPath) config.TerraformPath = fmt.Sprintf("%s/tools/terraform", config.K1FolderPath) config.HelmClientPath = fmt.Sprintf("%s/tools/helm", config.K1FolderPath) From 5b18d82ed7dcab4019f0ca88c5a0ec1809aacdb9 Mon Sep 17 00:00:00 2001 From: Jared Edwards Date: Fri, 15 Jul 2022 12:20:55 -0600 Subject: [PATCH 102/107] k-ray cluster create updates (#108) * merge conflicts * upstream merge conflict resolution * adopts new struct definition * new wait functionality for vault * remove unused vault code --- .gitignore | 3 +- cmd/create.go | 25 ++++----- cmd/createUtils.go | 109 +++++++++++++++++++++++++++++++++++--- cmd/destroy.go | 46 +++++++--------- cmd/kubefirstTemplate.go | 2 + internal/gitlab/gitlab.go | 19 +++---- 6 files changed, 150 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index 0dc59439d..a577106a4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ kubeconfig_* /git bin .vscode/settings.json -log +logs/ +/tmp lint_log.txt diff --git a/cmd/create.go b/cmd/create.go index 3a0de9e7a..d923ad563 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -3,6 +3,11 @@ package cmd import ( "bytes" "fmt" + "log" + "os/exec" + "syscall" + "time" + "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/internal/argocd" "github.com/kubefirst/kubefirst/internal/gitlab" @@ -14,10 +19,6 @@ import ( "github.com/kubefirst/kubefirst/internal/vault" "github.com/spf13/cobra" "github.com/spf13/viper" - "log" - "os/exec" - "syscall" - "time" ) // createCmd represents the create command @@ -146,7 +147,7 @@ to quickly create a Cobra application.`, //! progressPrinter.AddTracker("step-gitlab", "Setup Gitlab", 6) informUser("Waiting vault to be ready") - waitVaultToBeInitialized(dryRun) + waitVaultToBeRunning(dryRun) progressPrinter.IncrementTracker("step-gitlab", 1) if !dryRun { var kPortForwardVaultOutb, kPortForwardVaultErrb bytes.Buffer @@ -157,11 +158,13 @@ to quickly create a Cobra application.`, defer kPortForwardVault.Process.Signal(syscall.SIGTERM) if err != nil { // If it doesn't error, we kinda don't care much. - log.Println("Commad Execution STDOUT: %s", kPortForwardVaultOutb.String()) - log.Println("Commad Execution STDERR: %s", kPortForwardVaultErrb.String()) + log.Printf("Commad Execution STDOUT: %s", kPortForwardVaultOutb.String()) + log.Printf("Commad Execution STDERR: %s", kPortForwardVaultErrb.String()) log.Panicf("error: failed to port-forward to vault in main thread %s", err) } } + loopUntilPodIsReady() + initializeVaultAndAutoUnseal() informUser(fmt.Sprintf("Vault available at %s", viper.GetString("vault.local.service"))) progressPrinter.IncrementTracker("step-gitlab", 1) @@ -205,15 +208,13 @@ to quickly create a Cobra application.`, progressPrinter.AddTracker("step-vault", "Configure Vault", 4) informUser("waiting for vault unseal") - /** - */ - waitVaultToBeInitialized(dryRun) informUser("Vault initialized") progressPrinter.IncrementTracker("step-vault", 1) - waitForVaultUnseal(dryRun, config) - informUser("Vault unseal") + // todo need to make sure this is not needed + // waitForVaultUnseal(dryRun, config) + // informUser("Vault unseal") progressPrinter.IncrementTracker("step-vault", 1) log.Println("configuring vault") diff --git a/cmd/createUtils.go b/cmd/createUtils.go index 1411fb6b0..496860876 100644 --- a/cmd/createUtils.go +++ b/cmd/createUtils.go @@ -1,7 +1,14 @@ package cmd import ( + "encoding/json" "fmt" + "io/ioutil" + "log" + "net/http" + "strings" + "time" + "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/internal/progressPrinter" "github.com/kubefirst/kubefirst/internal/telemetry" @@ -9,8 +16,6 @@ import ( "github.com/spf13/viper" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" - "log" - "time" ) // todo: move it to internals/ArgoCD @@ -90,9 +95,9 @@ func waitArgoCDToBeReady(dryRun bool) { } } -func waitVaultToBeInitialized(dryRun bool) { +func waitVaultToBeRunning(dryRun bool) { if dryRun { - log.Printf("[#99] Dry-run mode, waitVaultToBeInitialized skipped.") + log.Printf("[#99] Dry-run mode, waitVaultToBeRunning skipped.") return } config := configs.ReadConfig() @@ -108,9 +113,11 @@ func waitVaultToBeInitialized(dryRun bool) { break } } + + //! failing x = 50 for i := 0; i < x; i++ { - _, _, err := pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "get", "pods", "-l", "vault-initialized=true") + _, _, 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) @@ -122,9 +129,99 @@ func waitVaultToBeInitialized(dryRun bool) { } } +func loopUntilPodIsReady() { + + x := 50 + url := "http://localhost:8200/v1/sys/health" + for i := 0; i < x; i++ { + log.Println("vault is not ready yet, sleeping and checking again") + 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) + continue + } + + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Println("vault is availbale but the body is not what is expected ", err) + continue + } + fmt.Println(string(body)) + + var responseJson map[string]interface{} + + if err := json.Unmarshal(body, &responseJson); err != nil { + log.Printf("vault is availbale but the body is not what is expected %s", err) + continue + } + isInitialized := responseJson["initialized"] + if !isInitialized.(bool) { + log.Printf("vault is initialized and is in the expected state") + break + } + } +} + +type VaultInitResponse struct { + Initialized bool `json:"initialized"` + Sealed bool `json:"sealed"` + Standby bool `json:"standby"` + PerformanceStandby bool `json:"performance_standby"` + ReplicationPerformanceMode string `json:"replication_performance_mode"` + ReplicationDrMode string `json:"replication_dr_mode"` + ServerTimeUtc int `json:"server_time_utc"` + Version string `json:"version"` +} + +type VaultUnsealResponse struct { + UnsealKeysB64 []interface{} `json:"unseal_keys_b64"` + UnsealKeysHex []interface{} `json:"unseal_keys_hex"` + UnsealShares int `json:"unseal_shares"` + UnsealThreshold int `json:"unseal_threshold"` + RecoveryKeysBase64 []string `json:"recovery_keys_base64"` + RecoveryKeys []string `json:"recovery_keys"` + RecoveryKeysShares int `json:"recovery_keys_shares"` + RecoveryKeysThreshold int `json:"recovery_keys_threshold"` + RootToken string `json:"root_token"` + Keys []string `json:"keys"` + KeysB64 []string `json:"keys_base64"` +} + +func initializeVaultAndAutoUnseal() { + url := "http://127.0.0.1:8200/v1/sys/init" + + payload := strings.NewReader("{\n\t\"stored_shares\": 3,\n\t\"recovery_threshold\": 3,\n\t\"recovery_shares\": 5\n}") + + req, _ := http.NewRequest("POST", url, payload) + + req.Header.Add("Content-Type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + log.Println("error in Do http client request") + } + + defer res.Body.Close() + body, _ := ioutil.ReadAll(res.Body) + + vaultResponse := VaultUnsealResponse{} + json.Unmarshal(body, &vaultResponse) + + viper.Set("vault.token", vaultResponse.RootToken) + viper.Set("vault.unseal-keys", vaultResponse) + viper.WriteConfig() +} + func waitGitlabToBeReady(dryRun bool) { if dryRun { - log.Printf("[#99] Dry-run mode, waitVaultToBeInitialized skipped.") + log.Printf("[#99] Dry-run mode, waitVaultToBeRunning skipped.") return } config := configs.ReadConfig() diff --git a/cmd/destroy.go b/cmd/destroy.go index f657a6776..e34c54471 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -2,15 +2,16 @@ package cmd import ( "bytes" + "log" + "os/exec" + "syscall" + "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/internal/aws" "github.com/kubefirst/kubefirst/internal/gitlab" "github.com/kubefirst/kubefirst/internal/k8s" "github.com/kubefirst/kubefirst/internal/terraform" "github.com/spf13/cobra" - "log" - "os/exec" - "syscall" ) // destroyCmd represents the destroy command @@ -43,17 +44,14 @@ if the registry has already been deleted.`, log.Panic(err) } - var kPortForwardOutb, kPortForwardErrb bytes.Buffer - kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") - kPortForward.Stdout = &kPortForwardOutb - kPortForward.Stderr = &kPortForwardErrb - defer kPortForward.Process.Signal(syscall.SIGTERM) - err = kPortForward.Start() - if err != nil { - log.Println("Commad Execution STDOUT: %s", kPortForwardOutb.String()) - log.Println("Commad Execution STDERR: %s", kPortForwardErrb.String()) - log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) - } + // kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + // kPortForward.Stdout = os.Stdout + // kPortForward.Stderr = os.Stderr + // defer kPortForward.Process.Signal(syscall.SIGTERM) + // err = kPortForward.Start() + // if err != nil { + // log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) + // } var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") @@ -66,18 +64,14 @@ if the registry has already been deleted.`, log.Println("Commad Execution STDERR: %s", kPortForwardArgocdErrb.String()) log.Panicf("error: failed to port-forward to argocd in main thread %s", err) } - - var kPortForwardVaultOutb, kPortForwardVaultErrb bytes.Buffer - kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") - kPortForwardVault.Stdout = &kPortForwardVaultOutb - kPortForwardVault.Stderr = &kPortForwardVaultErrb - err = kPortForwardVault.Start() - defer kPortForwardVault.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Println("Commad Execution STDOUT: %s", kPortForwardVaultOutb.String()) - log.Println("Commad Execution STDERR: %s", kPortForwardVaultErrb.String()) - log.Panicf("error: failed to port-forward to vault in main thread %s", err) - } + // kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") + // kPortForwardVault.Stdout = os.Stdout + // kPortForwardVault.Stderr = os.Stderr + // err = kPortForwardVault.Start() + // defer kPortForwardVault.Process.Signal(syscall.SIGTERM) + // if err != nil { + // log.Panicf("error: failed to port-forward to vault in main thread %s", err) + // } log.Println("destroying gitlab terraform") gitlab.DestroyGitlabTerraform(skipGitlabTerraform) diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 9bde96af1..6ae7afffc 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -121,6 +121,7 @@ func detokenizeDirectory(path string, fi os.FileInfo, err error) error { newContents = strings.Replace(string(read), "https://gitlab./kubefirst/gitops.git", "ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops", -1) } + argocdOidcClientId := viper.GetString(("gitlab.oidc.argocd.applicationid")) botPublicKey := viper.GetString("botpublickey") hostedZoneId := viper.GetString("aws.hostedzoneid") hostedZoneName := viper.GetString("aws.hostedzonename") @@ -140,6 +141,7 @@ func detokenizeDirectory(path string, fi os.FileInfo, err error) error { newContents = strings.Replace(newContents, "", bucketArgoArtifacts, -1) newContents = strings.Replace(newContents, "", bucketGitlabBackup, -1) newContents = strings.Replace(newContents, "", bucketChartmuseum, -1) + newContents = strings.Replace(newContents, "", argocdOidcClientId, -1) newContents = strings.Replace(newContents, "", hostedZoneId, -1) newContents = strings.Replace(newContents, "", hostedZoneName, -1) newContents = strings.Replace(newContents, "", region, -1) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index c4cb695d3..265d32e90 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -10,6 +10,14 @@ import ( "encoding/json" "encoding/pem" "fmt" + "html/template" + "log" + "net/http" + "net/url" + "os" + "strings" + "time" + "github.com/ghodss/yaml" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" @@ -20,17 +28,10 @@ import ( "github.com/kubefirst/kubefirst/internal/k8s" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" - "html/template" v1 "k8s.io/api/core/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" - "log" - "net/http" - "net/url" - "os" - "strings" - "time" "golang.org/x/crypto/ssh" ) @@ -94,7 +95,7 @@ func PushGitOpsToGitLab(dryRun bool) { repo, err := git.PlainOpen(directory) if err != nil { - log.Panicf("error opening the directory ", directory, err) + log.Panicf("error opening the directory %s: %s", directory, err) } upstream := fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", domain) @@ -330,7 +331,7 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { envs["TF_VAR_aws_region"] = viper.GetString("aws.region") envs["TF_VAR_hosted_zone_name"] = viper.GetString("aws.hostedzonename") - directory := fmt.Sprintf("%s/.gitops/terraform/gitlab", config.K1FolderPath) + directory := fmt.Sprintf("%s/gitops/terraform/gitlab", config.K1FolderPath) err := os.Chdir(directory) if err != nil { log.Panicf("error: could not change directory to " + directory) From 8c196a1eaca7147bf6b1f9ad8a17cc152a2b24fe Mon Sep 17 00:00:00 2001 From: John Dietz Date: Fri, 15 Jul 2022 17:36:09 -0400 Subject: [PATCH 103/107] fixes for vault unseal (#114) --- cmd/createUtils.go | 8 +++++--- internal/vault/vault.go | 33 +++++++-------------------------- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/cmd/createUtils.go b/cmd/createUtils.go index 496860876..e132d6fb2 100644 --- a/cmd/createUtils.go +++ b/cmd/createUtils.go @@ -161,12 +161,14 @@ func loopUntilPodIsReady() { log.Printf("vault is availbale but the body is not what is expected %s", err) continue } - isInitialized := responseJson["initialized"] - if !isInitialized.(bool) { + + _, ok := responseJson["initialized"] + if ok { log.Printf("vault is initialized and is in the expected state") - break + return } } + log.Panic("vault was never initialized") } type VaultInitResponse struct { diff --git a/internal/vault/vault.go b/internal/vault/vault.go index b96345fe8..8cdd677b6 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -5,20 +5,18 @@ import ( "context" "encoding/json" "fmt" + "log" + "os" + "os/exec" + "syscall" + vault "github.com/hashicorp/vault/api" "github.com/kubefirst/kubefirst/configs" - "github.com/kubefirst/kubefirst/internal/k8s" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" gitlab "github.com/xanzy/go-gitlab" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/clientcmd" - "log" - "os" - "os/exec" - "syscall" ) // GetVaultRootToken get `vault-unseal-keys` token on Vault. @@ -61,29 +59,12 @@ func ConfigureVault(dryRun bool) { // ``` // ... obviously keep the sensitive values bound to vars - k8sClient, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) - if err != nil { - log.Panicf("error: getting k8sClient %s", err) - } - clientset, err := kubernetes.NewForConfig(k8sClient) - if err != nil { - log.Panicf("error: getting k8sClient &s", err) - } - - k8s.VaultSecretClient = clientset.CoreV1().Secrets("vault") - vaultToken, err := GetVaultRootToken(k8s.VaultSecretClient) - if err != nil { - log.Panicf("unable to get vault root token, error: %s", err) - } - - viper.Set("vault.token", vaultToken) - viper.WriteConfig() - + vaultToken := viper.GetString("vault.token") var kPortForwardOutb, kPortForwardErrb bytes.Buffer kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") kPortForward.Stdout = &kPortForwardOutb kPortForward.Stderr = &kPortForwardErrb - err = kPortForward.Start() + err := kPortForward.Start() defer kPortForward.Process.Signal(syscall.SIGTERM) if err != nil { log.Println("Commad Execution STDOUT: %s", kPortForwardOutb.String()) From 3c00bd1488ba87e8a4972377f1e57b00e04a08e4 Mon Sep 17 00:00:00 2001 From: John Dietz Date: Fri, 15 Jul 2022 18:56:01 -0400 Subject: [PATCH 104/107] Vault unseal (#117) * fixes for vault unseal * not my favorite commit * remove not my stuff --- cmd/create.go | 36 +++++++++++++++++++++++------------- internal/gitlab/gitlab.go | 8 +++++++- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index d923ad563..28cd03cb8 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -13,7 +13,6 @@ import ( "github.com/kubefirst/kubefirst/internal/gitlab" "github.com/kubefirst/kubefirst/internal/helm" "github.com/kubefirst/kubefirst/internal/progressPrinter" - "github.com/kubefirst/kubefirst/internal/reports" "github.com/kubefirst/kubefirst/internal/softserve" "github.com/kubefirst/kubefirst/internal/terraform" "github.com/kubefirst/kubefirst/internal/vault" @@ -21,6 +20,7 @@ import ( "github.com/spf13/viper" ) + // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", @@ -49,11 +49,7 @@ to quickly create a Cobra application.`, log.Panic(err) } - // todo: - // isolate commands, in case we want to run some validations on the create, it would be a good idea to call the - // functions that does the validations infoCmd.Run(cmd, args) - progressPrinter.IncrementTracker("step-0", 1) progressPrinter.AddTracker("step-softserve", "Prepare Temporary Repo ", 4) @@ -208,13 +204,15 @@ to quickly create a Cobra application.`, progressPrinter.AddTracker("step-vault", "Configure Vault", 4) informUser("waiting for vault unseal") + /** - informUser("Vault initialized") + */ + waitVaultToBeRunning(dryRun) + informUser("Vault running") progressPrinter.IncrementTracker("step-vault", 1) - // todo need to make sure this is not needed - // waitForVaultUnseal(dryRun, config) - // informUser("Vault unseal") + waitForVaultUnseal(dryRun, config) + informUser("Vault unseal") progressPrinter.IncrementTracker("step-vault", 1) log.Println("configuring vault") @@ -226,7 +224,9 @@ to quickly create a Cobra application.`, createVaultConfiguredSecret(dryRun, config) informUser("Vault secret created") progressPrinter.IncrementTracker("step-vault", 1) + } + if !viper.GetBool("gitlab.oidc-created") { progressPrinter.AddTracker("step-post-gitlab", "Finalize Gitlab updates", 5) vault.AddGitlabOidcApplications(dryRun) informUser("Added Gitlab OIDC") @@ -238,24 +238,34 @@ to quickly create a Cobra application.`, informUser("Pushing gitops repo to origin gitlab") // refactor: sounds like a new functions, should PushGitOpsToGitLab be renamed/update signature? - + viper.Set("gitlab.oidc-created", true) + viper.WriteConfig() + } + if !viper.GetBool("gitlab.gitops-pushed") { gitlab.PushGitRepo(dryRun, config, "gitlab", "gitops") // todo: need to handle if this was already pushed, errors on failure) progressPrinter.IncrementTracker("step-post-gitlab", 1) // todo: keep one of the two git push functions, they're similar, but not exactly the same //gitlab.PushGitOpsToGitLab(dryRun) - + viper.Set("gitlab.gitops-pushed", true) + viper.WriteConfig() + } + if !viper.GetBool("gitlab.metaphor-pushed") { informUser("Pushing metaphor repo to origin gitlab") gitlab.PushGitRepo(dryRun, config, "gitlab", "metaphor") progressPrinter.IncrementTracker("step-post-gitlab", 1) // todo: keep one of the two git push functions, they're similar, but not exactly the same //gitlab.PushGitOpsToGitLab(dryRun) - + viper.Set("gitlab.metaphor-pushed", true) + viper.WriteConfig() + } + if !viper.GetBool("gitlab.registered") { informUser("Changing registry to Gitlab") gitlab.ChangeRegistryToGitLab(dryRun) progressPrinter.IncrementTracker("step-post-gitlab", 1) - // todo triage / force apply the contents adjusting // todo kind: Application .repoURL: + viper.Set("gitlab.registered", true) + viper.WriteConfig() } } sendCompleteInstallTelemetry(dryRun) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index 265d32e90..c2ae2f84c 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -560,7 +560,13 @@ func PushGitRepo(dryRun bool, config *configs.Config, gitOrigin, repoName string } if gitOrigin == "gitlab" { - + pkg.Detokenize(repoDir) + os.RemoveAll(repoDir + "/terraform/base/.terraform") + os.RemoveAll(repoDir + "/terraform/gitlab/.terraform") + os.RemoveAll(repoDir + "/terraform/vault/.terraform") + os.Remove(repoDir + "/terraform/base/.terraform.lock.hcl") + os.Remove(repoDir + "/terraform/gitlab/.terraform.lock.hcl") + CommitToRepo(repo, repoName) auth := &gitHttp.BasicAuth{ Username: "root", Password: viper.GetString("gitlab.token"), From 0206148017fdf02c3b9fddb1baa0ed2ae7b60ca4 Mon Sep 17 00:00:00 2001 From: John Dietz Date: Fri, 15 Jul 2022 18:57:45 -0400 Subject: [PATCH 105/107] Vault unseal (#118) * fixes for vault unseal * not my favorite commit * remove not my stuff * add reports package back From a7fae7c83f58ab028d96fe1d108c59771cfc4d85 Mon Sep 17 00:00:00 2001 From: John Dietz Date: Fri, 15 Jul 2022 19:18:48 -0400 Subject: [PATCH 106/107] Vault unseal (#119) * fixes for vault unseal * not my favorite commit * remove not my stuff * add reports package back * viper check adjustments * viper check adjustments --- internal/gitlab/gitlab.go | 169 ++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 87 deletions(-) diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index c2ae2f84c..b0d88b697 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -359,105 +359,100 @@ func DestroyGitlabTerraform(skipGitlabTerraform bool) { func ChangeRegistryToGitLab(dryRun bool) { config := configs.ReadConfig() - if !viper.GetBool("gitlab.registry") { - if dryRun { - log.Printf("[#99] Dry-run mode, ChangeRegistryToGitLab skipped.") - return - } - type ArgocdGitCreds struct { - PersonalAccessToken string - URL string - FullURL string - } + if dryRun { + log.Printf("[#99] Dry-run mode, ChangeRegistryToGitLab skipped.") + return + } - pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) - url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.hostedzonename")))) - fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")))) + type ArgocdGitCreds struct { + PersonalAccessToken string + URL string + FullURL string + } - creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} + pat := b64.StdEncoding.EncodeToString([]byte(viper.GetString("gitlab.token"))) + url := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/", viper.GetString("aws.hostedzonename")))) + fullurl := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("https://gitlab.%s/kubefirst/gitops.git", viper.GetString("aws.hostedzonename")))) - var argocdRepositoryAccessTokenSecret *v1.Secret - k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) - if err != nil { - log.Panicf("error getting client from kubeconfig") - } - clientset, err := kubernetes.NewForConfig(k8sConfig) - if err != nil { - log.Panicf("error getting kubeconfig for clientset") - } - k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") - - var secrets bytes.Buffer - - c, err := template.New("creds-gitlab").Parse(` - apiVersion: v1 - data: - password: {{ .PersonalAccessToken }} - url: {{ .URL }} - username: cm9vdA== - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repo-creds - name: creds-gitlab - namespace: argocd - type: Opaque - `) - if err := c.Execute(&secrets, creds); err != nil { - log.Panicf("error executing golang template for git repository credentials template %s", err) - } + creds := ArgocdGitCreds{PersonalAccessToken: pat, URL: url, FullURL: fullurl} + + var argocdRepositoryAccessTokenSecret *v1.Secret + k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) + if err != nil { + log.Panicf("error getting client from kubeconfig") + } + clientset, err := kubernetes.NewForConfig(k8sConfig) + if err != nil { + log.Panicf("error getting kubeconfig for clientset") + } + k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") - ba := []byte(secrets.String()) - err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + var secrets bytes.Buffer + + c, err := template.New("creds-gitlab").Parse(` + apiVersion: v1 + data: + password: {{ .PersonalAccessToken }} + url: {{ .URL }} + username: cm9vdA== + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repo-creds + name: creds-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&secrets, creds); err != nil { + log.Panicf("error executing golang template for git repository credentials template %s", err) + } - _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) - if err != nil { - log.Panicf("error creating argocd repository credentials template secret %s", err) - } + ba := []byte(secrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - var repoSecrets bytes.Buffer - - c, err = template.New("repo-gitlab").Parse(` - apiVersion: v1 - data: - project: ZGVmYXVsdA== - type: Z2l0 - url: {{ .FullURL }} - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repository - name: repo-gitlab - namespace: argocd - type: Opaque - `) - if err := c.Execute(&repoSecrets, creds); err != nil { - log.Panicf("error executing golang template for gitops repository template %s", err) - } + _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + log.Panicf("error creating argocd repository credentials template secret %s", err) + } - ba = []byte(repoSecrets.String()) - err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + var repoSecrets bytes.Buffer + + c, err = template.New("repo-gitlab").Parse(` + apiVersion: v1 + data: + project: ZGVmYXVsdA== + type: Z2l0 + url: {{ .FullURL }} + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repository + name: repo-gitlab + namespace: argocd + type: Opaque + `) + if err := c.Execute(&repoSecrets, creds); err != nil { + log.Panicf("error executing golang template for gitops repository template %s", err) + } - _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) - if err != nil { - log.Panicf("error creating argocd repository connection secret %s", err) - } + ba = []byte(repoSecrets.String()) + err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) - _, _, err = pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.K1FolderPath)) - if err != nil { - log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) - } + _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) + if err != nil { + log.Panicf("error creating argocd repository connection secret %s", err) + } - viper.Set("gitlab.registry", true) - viper.WriteConfig() - } else { - log.Println("Skipping: ChangeRegistryToGitLab") + _, _, err = pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.K1FolderPath)) + if err != nil { + log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) } + } func HydrateGitlabMetaphorRepo(dryRun bool) { From 79d1281594ffdb3c5371472291f82b39829cb7b4 Mon Sep 17 00:00:00 2001 From: John Dietz Date: Tue, 19 Jul 2022 02:53:13 -0400 Subject: [PATCH 107/107] Vault unseal (#120) * fixes for vault unseal * not my favorite commit * remove not my stuff * add reports package back * viper check adjustments * viper check adjustments * vault local address * changes, mostly formatting * spaces * adjustments through latest provisioning * Vault unseal tweak 0718 (#121) * allow the retry to work as expected * re-enable dry-run create Signed-off-by: 6za <53096417+6za@users.noreply.github.com> * adjustments for gitlab takeover * unused ref * detokenization games * add slash to .git/ on detokenize denylist * adding registry sync after gitlab gitops registry * terraform lock removal * adding argocd app host check and recycling/resyncing * addressinging orchestration issues from last run * port-forward fix Co-authored-by: jarededwards Co-authored-by: Cesar Filho <53096417+6za@users.noreply.github.com> --- cmd/argocdSync.go | 13 +- cmd/checktools.go | 7 +- cmd/create.go | 246 ++++++++++++++++++++++++------------- cmd/createUtils.go | 14 ++- cmd/destroy.go | 21 ++-- cmd/kubefirstTemplate.go | 15 +-- cmd/kubernetes.go | 36 ++++-- internal/argocd/argocd.go | 37 +++++- internal/gitlab/gitlab.go | 138 ++++++++++++++------- internal/k8s/kubernetes.go | 29 ++++- internal/vault/vault.go | 2 +- pkg/helpers.go | 10 +- pkg/keys.go | 28 ----- 13 files changed, 387 insertions(+), 209 deletions(-) diff --git a/cmd/argocdSync.go b/cmd/argocdSync.go index 797806803..38d19605a 100644 --- a/cmd/argocdSync.go +++ b/cmd/argocdSync.go @@ -1,9 +1,10 @@ package cmd import ( - "github.com/kubefirst/kubefirst/internal/argocd" "log" + "github.com/kubefirst/kubefirst/internal/argocd" + "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -20,10 +21,12 @@ This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { - dryRun, err := cmd.Flags().GetBool("dry-run") - if err != nil { - log.Panic(err) - } + // dryRun, err := cmd.Flags().GetBool("dry-run") + // if err != nil { + // log.Panic(err) + // } + + dryRun := false log.Println("dry run enabled:", dryRun) diff --git a/cmd/checktools.go b/cmd/checktools.go index 7592ae4ad..1b2319d53 100644 --- a/cmd/checktools.go +++ b/cmd/checktools.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" @@ -27,13 +28,13 @@ var checktoolsCmd = &cobra.Command{ fmt.Printf("-> helm version:\n\t%s\n\t%s\n", helmVersion, helmStdErr) if errKubectl != nil { - fmt.Println("failed to call kubectlVersionCmd.Run(): %v", errKubectl) + fmt.Printf("failed to call kubectlVersionCmd.Run(): %v", errKubectl) } if errHelm != nil { - fmt.Println("failed to call helmVersionCmd.Run(): %v", errHelm) + fmt.Printf("failed to call helmVersionCmd.Run(): %v", errHelm) } if errTerraform != nil { - fmt.Println("failed to call terraformVersionCmd.Run(): %v", errTerraform) + fmt.Printf("failed to call terraformVersionCmd.Run(): %v", errTerraform) } }, diff --git a/cmd/create.go b/cmd/create.go index 28cd03cb8..fbc6959f9 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -12,15 +12,19 @@ import ( "github.com/kubefirst/kubefirst/internal/argocd" "github.com/kubefirst/kubefirst/internal/gitlab" "github.com/kubefirst/kubefirst/internal/helm" + "github.com/kubefirst/kubefirst/internal/k8s" "github.com/kubefirst/kubefirst/internal/progressPrinter" + "github.com/kubefirst/kubefirst/internal/reports" "github.com/kubefirst/kubefirst/internal/softserve" "github.com/kubefirst/kubefirst/internal/terraform" "github.com/kubefirst/kubefirst/internal/vault" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/cobra" "github.com/spf13/viper" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" ) - // createCmd represents the create command var createCmd = &cobra.Command{ Use: "create", @@ -81,8 +85,8 @@ to quickly create a Cobra application.`, defer kPortForwardSoftServe.Process.Signal(syscall.SIGTERM) if err != nil { // If it doesn't error, we kinda don't care much. - log.Println("Commad Execution STDOUT: %s", kPortForwardSoftServeOutb.String()) - log.Println("Commad Execution STDERR: %s", kPortForwardSoftServeErrb.String()) + log.Printf("Commad Execution STDOUT: %s", kPortForwardSoftServeOutb.String()) + log.Printf("Commad Execution STDERR: %s", kPortForwardSoftServeErrb.String()) log.Panicf("error: failed to port-forward to soft-serve %s", err) } time.Sleep(20 * time.Second) @@ -101,22 +105,22 @@ to quickly create a Cobra application.`, waitArgoCDToBeReady(dryRun) informUser("ArgoCD Ready") progressPrinter.IncrementTracker("step-argo", 1) - if !dryRun { - var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer - kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") - kPortForwardArgocd.Stdout = &kPortForwardArgocdOutb - kPortForwardArgocd.Stderr = &kPortForwardArgocdErrb - err = kPortForwardArgocd.Start() - defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) - if err != nil { - log.Println("Commad Execution STDOUT: %s", kPortForwardArgocdOutb.String()) - log.Println("Commad Execution STDERR: %s", kPortForwardArgocdErrb.String()) - log.Panicf("error: failed to port-forward to argocd in main thread %s", err) - } - log.Println("sleeping for 45 seconds, hurry up jared") - time.Sleep(45 * time.Second) + var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer + kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") + kPortForwardArgocd.Stdout = &kPortForwardArgocdOutb + kPortForwardArgocd.Stderr = &kPortForwardArgocdErrb + err = kPortForwardArgocd.Start() + defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Printf("Commad Execution STDOUT: %s", kPortForwardArgocdOutb.String()) + log.Printf("Commad Execution STDERR: %s", kPortForwardArgocdErrb.String()) + log.Panicf("error: failed to port-forward to argocd in main thread %s", err) } + + // log.Println("sleeping for 45 seconds, hurry up jared") + // time.Sleep(45 * time.Second) + informUser(fmt.Sprintf("ArgoCD available at %s", viper.GetString("argocd.local.service"))) progressPrinter.IncrementTracker("step-argo", 1) @@ -128,6 +132,13 @@ to quickly create a Cobra application.`, token := argocd.GetArgocdAuthToken(dryRun) progressPrinter.IncrementTracker("step-argo", 1) + _, _, err = pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/helpers/registry.yaml", config.K1FolderPath)) + if err != nil { + log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) + } + time.Sleep(45 * time.Second) + //TODO: ensure argocd is in a good heathy state before syncing the registry application + informUser("Syncing the registry application") argocd.SyncArgocdApplication(dryRun, "registry", token) progressPrinter.IncrementTracker("step-argo", 1) @@ -135,8 +146,8 @@ to quickly create a Cobra application.`, // todo, need to stall until the registry has synced, then get to ui asap //! skip this if syncing from argocd and not helm installing - log.Printf("sleeping for 30 seconds, hurry up jared sign into argocd %s", viper.GetString("argocd.admin.password")) - time.Sleep(30 * time.Second) + // log.Printf("sleeping for 30 seconds, hurry up jared sign into argocd %s", viper.GetString("argocd.admin.password")) + // time.Sleep(30 * time.Second) //! //* we need to stop here and wait for the vault namespace to exist and the vault pod to be ready @@ -159,8 +170,8 @@ to quickly create a Cobra application.`, log.Panicf("error: failed to port-forward to vault in main thread %s", err) } } - loopUntilPodIsReady() - initializeVaultAndAutoUnseal() + loopUntilPodIsReady(dryRun) + initializeVaultAndAutoUnseal(dryRun) informUser(fmt.Sprintf("Vault available at %s", viper.GetString("vault.local.service"))) progressPrinter.IncrementTracker("step-gitlab", 1) @@ -180,8 +191,8 @@ to quickly create a Cobra application.`, defer kPortForwardGitlab.Process.Signal(syscall.SIGTERM) if err != nil { // If it doesn't error, we kinda don't care much. - log.Println("Commad Execution STDOUT: %s", kPortForwardGitlabOutb.String()) - log.Println("Commad Execution STDERR: %s", kPortForwardGitlabErrb.String()) + log.Printf("Commad Execution STDOUT: %s", kPortForwardGitlabOutb.String()) + log.Printf("Commad Execution STDERR: %s", kPortForwardGitlabErrb.String()) log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) } } @@ -199,75 +210,146 @@ to quickly create a Cobra application.`, gitlab.GitlabKeyUpload(dryRun) informUser("Gitlab ready") progressPrinter.IncrementTracker("step-gitlab", 1) + } + if !skipVault { + + progressPrinter.AddTracker("step-vault", "Configure Vault", 4) + informUser("waiting for vault unseal") + /** + + */ + waitVaultToBeRunning(dryRun) + informUser("Vault running") + progressPrinter.IncrementTracker("step-vault", 1) + + waitForVaultUnseal(dryRun, config) + informUser("Vault unseal") + progressPrinter.IncrementTracker("step-vault", 1) + + log.Println("configuring vault") + vault.ConfigureVault(dryRun) + informUser("Vault configured") + progressPrinter.IncrementTracker("step-vault", 1) + + log.Println("creating vault configured secret") + createVaultConfiguredSecret(dryRun, config) + informUser("Vault secret created") + progressPrinter.IncrementTracker("step-vault", 1) + } - if !skipVault { - - progressPrinter.AddTracker("step-vault", "Configure Vault", 4) - informUser("waiting for vault unseal") - /** - - */ - waitVaultToBeRunning(dryRun) - informUser("Vault running") - progressPrinter.IncrementTracker("step-vault", 1) - - waitForVaultUnseal(dryRun, config) - informUser("Vault unseal") - progressPrinter.IncrementTracker("step-vault", 1) + if !viper.GetBool("gitlab.oidc-created") { + progressPrinter.AddTracker("step-post-gitlab", "Finalize Gitlab updates", 5) + vault.AddGitlabOidcApplications(dryRun) + informUser("Added Gitlab OIDC") + progressPrinter.IncrementTracker("step-post-gitlab", 1) - log.Println("configuring vault") - vault.ConfigureVault(dryRun) - informUser("Vault configured") - progressPrinter.IncrementTracker("step-vault", 1) + informUser("Waiting for Gitlab dns to propagate before continuing") + gitlab.AwaitHost("gitlab", dryRun) + progressPrinter.IncrementTracker("step-post-gitlab", 1) - log.Println("creating vault configured secret") - createVaultConfiguredSecret(dryRun, config) - informUser("Vault secret created") - progressPrinter.IncrementTracker("step-vault", 1) + informUser("Pushing gitops repo to origin gitlab") + // refactor: sounds like a new functions, should PushGitOpsToGitLab be renamed/update signature? + viper.Set("gitlab.oidc-created", true) + viper.WriteConfig() + } + if !viper.GetBool("gitlab.gitops-pushed") { + gitlab.PushGitRepo(dryRun, config, "gitlab", "gitops") // todo: need to handle if this was already pushed, errors on failure) + progressPrinter.IncrementTracker("step-post-gitlab", 1) + // todo: keep one of the two git push functions, they're similar, but not exactly the same + //gitlab.PushGitOpsToGitLab(dryRun) + viper.Set("gitlab.gitops-pushed", true) + viper.WriteConfig() + } + if !dryRun && !viper.GetBool("argocd.oidc-patched") { + cfg := configs.ReadConfig() + config, err := clientcmd.BuildConfigFromFlags("", cfg.KubeConfigPath) + if err != nil { + panic(err.Error()) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) } - if !viper.GetBool("gitlab.oidc-created") { - progressPrinter.AddTracker("step-post-gitlab", "Finalize Gitlab updates", 5) - vault.AddGitlabOidcApplications(dryRun) - informUser("Added Gitlab OIDC") - progressPrinter.IncrementTracker("step-post-gitlab", 1) - - informUser("Waiting for Gitlab dns to propagate before continuing") - gitlab.AwaitGitlab(dryRun) - progressPrinter.IncrementTracker("step-post-gitlab", 1) + argocdSecretClient = clientset.CoreV1().Secrets("argocd") + patchSecret(argocdSecretClient, "argocd-secret", "oidc.gitlab.clientSecret", viper.GetString("gitlab.oidc.argocd.secret")) - informUser("Pushing gitops repo to origin gitlab") - // refactor: sounds like a new functions, should PushGitOpsToGitLab be renamed/update signature? - viper.Set("gitlab.oidc-created", true) - viper.WriteConfig() - } - if !viper.GetBool("gitlab.gitops-pushed") { - gitlab.PushGitRepo(dryRun, config, "gitlab", "gitops") // todo: need to handle if this was already pushed, errors on failure) - progressPrinter.IncrementTracker("step-post-gitlab", 1) - // todo: keep one of the two git push functions, they're similar, but not exactly the same - //gitlab.PushGitOpsToGitLab(dryRun) - viper.Set("gitlab.gitops-pushed", true) - viper.WriteConfig() + argocdPodClient := clientset.CoreV1().Pods("argocd") + argocdPodName := k8s.GetPodNameByLabel(argocdPodClient, "app.kubernetes.io/name=argocd-server") + k8s.DeletePodByName(argocdPodClient, argocdPodName) + viper.Set("argocd.oidc-patched", true) + viper.WriteConfig() + } + if !viper.GetBool("gitlab.metaphor-pushed") { + informUser("Pushing metaphor repo to origin gitlab") + gitlab.PushGitRepo(dryRun, config, "gitlab", "metaphor") + progressPrinter.IncrementTracker("step-post-gitlab", 1) + // todo: keep one of the two git push functions, they're similar, but not exactly the same + //gitlab.PushGitOpsToGitLab(dryRun) + viper.Set("gitlab.metaphor-pushed", true) + viper.WriteConfig() + } + if !viper.GetBool("gitlab.registered") { + // informUser("Getting ArgoCD auth token") + // token := argocd.GetArgocdAuthToken(dryRun) + // progressPrinter.IncrementTracker("step-post-gitlab", 1) + + // informUser("Detaching the registry application from softserve") + // argocd.DeleteArgocdApplicationNoCascade(dryRun, "registry", token) + // progressPrinter.IncrementTracker("step-post-gitlab", 1) + + informUser("Adding the registry application registered against gitlab") + gitlab.ChangeRegistryToGitLab(dryRun) + progressPrinter.IncrementTracker("step-post-gitlab", 1) + // todo triage / force apply the contents adjusting + // todo kind: Application .repoURL: + + // informUser("Waiting for argocd host to resolve") + // gitlab.AwaitHost("argocd", dryRun) + cfg := configs.ReadConfig() + config, err := clientcmd.BuildConfigFromFlags("", cfg.KubeConfigPath) + if err != nil { + panic(err.Error()) } - if !viper.GetBool("gitlab.metaphor-pushed") { - informUser("Pushing metaphor repo to origin gitlab") - gitlab.PushGitRepo(dryRun, config, "gitlab", "metaphor") - progressPrinter.IncrementTracker("step-post-gitlab", 1) - // todo: keep one of the two git push functions, they're similar, but not exactly the same - //gitlab.PushGitOpsToGitLab(dryRun) - viper.Set("gitlab.metaphor-pushed", true) - viper.WriteConfig() + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) } - if !viper.GetBool("gitlab.registered") { - informUser("Changing registry to Gitlab") - gitlab.ChangeRegistryToGitLab(dryRun) - progressPrinter.IncrementTracker("step-post-gitlab", 1) - // todo triage / force apply the contents adjusting - // todo kind: Application .repoURL: - viper.Set("gitlab.registered", true) - viper.WriteConfig() + argocdPodClient := clientset.CoreV1().Pods("argocd") + argocdPodName := k8s.GetPodNameByLabel(argocdPodClient, "app.kubernetes.io/name=argocd-server") + kPortForwardArgocd.Process.Signal(syscall.SIGTERM) + informUser("deleting argocd-server pod") + k8s.DeletePodByName(argocdPodClient, argocdPodName) + informUser("waiting for argocd to be ready") + waitArgoCDToBeReady(dryRun) + + informUser("Port forwarding to new argocd-server pod") + if !dryRun { + time.Sleep(time.Second * 20) + var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer + config := configs.ReadConfig() + kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") + kPortForwardArgocd.Stdout = &kPortForwardArgocdOutb + kPortForwardArgocd.Stderr = &kPortForwardArgocdErrb + err = kPortForwardArgocd.Start() + defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) + if err != nil { + log.Printf("Commad Execution STDOUT: %s", kPortForwardArgocdOutb.String()) + log.Printf("Commad Execution STDERR: %s", kPortForwardArgocdErrb.String()) + log.Panicf("error: failed to port-forward to argocd in main thread %s", err) + } + log.Println("sleeping for 40 seconds") + time.Sleep(40 * time.Second) } + + informUser("Syncing the registry application") + token := argocd.GetArgocdAuthToken(dryRun) + argocd.SyncArgocdApplication(dryRun, "registry", token) + + viper.Set("gitlab.registered", true) + viper.WriteConfig() } + sendCompleteInstallTelemetry(dryRun) time.Sleep(time.Millisecond * 100) diff --git a/cmd/createUtils.go b/cmd/createUtils.go index e132d6fb2..f3425ffd1 100644 --- a/cmd/createUtils.go +++ b/cmd/createUtils.go @@ -129,7 +129,11 @@ func waitVaultToBeRunning(dryRun bool) { } } -func loopUntilPodIsReady() { +func loopUntilPodIsReady(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, loopUntilPodIsReady skipped.") + return + } x := 50 url := "http://localhost:8200/v1/sys/health" @@ -153,7 +157,7 @@ func loopUntilPodIsReady() { log.Println("vault is availbale but the body is not what is expected ", err) continue } - fmt.Println(string(body)) + log.Println(string(body)) var responseJson map[string]interface{} @@ -196,7 +200,11 @@ type VaultUnsealResponse struct { KeysB64 []string `json:"keys_base64"` } -func initializeVaultAndAutoUnseal() { +func initializeVaultAndAutoUnseal(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, initializeVaultAndAutoUnseal skipped.") + return + } url := "http://127.0.0.1:8200/v1/sys/init" payload := strings.NewReader("{\n\t\"stored_shares\": 3,\n\t\"recovery_threshold\": 3,\n\t\"recovery_shares\": 5\n}") diff --git a/cmd/destroy.go b/cmd/destroy.go index e34c54471..c55d1d5e5 100644 --- a/cmd/destroy.go +++ b/cmd/destroy.go @@ -3,6 +3,7 @@ package cmd import ( "bytes" "log" + "os" "os/exec" "syscall" @@ -44,14 +45,14 @@ if the registry has already been deleted.`, log.Panic(err) } - // kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") - // kPortForward.Stdout = os.Stdout - // kPortForward.Stderr = os.Stderr - // defer kPortForward.Process.Signal(syscall.SIGTERM) - // err = kPortForward.Start() - // if err != nil { - // log.Panicf("error: failed to port-forward to gitlab in main thread %s", err) - // } + kPortForward := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "gitlab", "port-forward", "svc/gitlab-webservice-default", "8888:8080") + kPortForward.Stdout = os.Stdout + kPortForward.Stderr = os.Stderr + defer kPortForward.Process.Signal(syscall.SIGTERM) + err = kPortForward.Start() + if err != nil { + log.Printf("warning: failed to port-forward to gitlab in main thread %s", err) + } var kPortForwardArgocdOutb, kPortForwardArgocdErrb bytes.Buffer kPortForwardArgocd := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "port-forward", "svc/argocd-server", "8080:80") @@ -60,8 +61,8 @@ if the registry has already been deleted.`, err = kPortForwardArgocd.Start() defer kPortForwardArgocd.Process.Signal(syscall.SIGTERM) if err != nil { - log.Println("Commad Execution STDOUT: %s", kPortForwardArgocdOutb.String()) - log.Println("Commad Execution STDERR: %s", kPortForwardArgocdErrb.String()) + log.Printf("Commad Execution STDOUT: %s", kPortForwardArgocdOutb.String()) + log.Printf("Commad Execution STDERR: %s", kPortForwardArgocdErrb.String()) log.Panicf("error: failed to port-forward to argocd in main thread %s", err) } // kPortForwardVault := exec.Command(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "vault", "port-forward", "svc/vault", "8200:8200") diff --git a/cmd/kubefirstTemplate.go b/cmd/kubefirstTemplate.go index 6ae7afffc..0d0a09ae2 100644 --- a/cmd/kubefirstTemplate.go +++ b/cmd/kubefirstTemplate.go @@ -14,6 +14,7 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/kubefirst/kubefirst/configs" + "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" ) @@ -42,7 +43,7 @@ func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName st log.Printf("cloned %s-template repository to directory %s/%s", repoName, config.K1FolderPath, repoName) log.Printf("detokenizing %s/%s", config.K1FolderPath, repoName) - detokenize(directory) + pkg.Detokenize(directory) log.Printf("detokenization of %s/%s complete", config.K1FolderPath, repoName) viper.Set(fmt.Sprintf("init.repos.%s.detokenized", repoName), true) @@ -78,13 +79,13 @@ func prepareKubefirstTemplateRepo(config *configs.Config, githubOrg, repoName st viper.WriteConfig() } -func detokenize(path string) { +// func detokenize(path string) { - err := filepath.Walk(path, detokenizeDirectory) - if err != nil { - panic(err) - } -} +// err := filepath.Walk(path, detokenizeDirectory) +// if err != nil { +// panic(err) +// } +// } func detokenizeDirectory(path string, fi os.FileInfo, err error) error { if err != nil { diff --git a/cmd/kubernetes.go b/cmd/kubernetes.go index 0a55ac04b..c2ce85624 100644 --- a/cmd/kubernetes.go +++ b/cmd/kubernetes.go @@ -9,15 +9,16 @@ import ( "context" "encoding/json" "fmt" + "log" + "os" + "os/exec" + "time" + "github.com/kubefirst/kubefirst/configs" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" - "log" - "os" - "os/exec" - "time" ) var vaultRootToken string @@ -29,16 +30,16 @@ var vaultSecretClient coreV1Types.SecretInterface var argocdSecretClient coreV1Types.SecretInterface var gitlabPodsClient coreV1Types.PodInterface -func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { - pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) - if err != nil { - fmt.Println(err) - } +// func getPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { +// pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: label}) +// if err != nil { +// fmt.Println(err) +// } - gitlabToolboxPodName = pods.Items[0].Name +// gitlabToolboxPodName = pods.Items[0].Name - return gitlabToolboxPodName -} +// return gitlabToolboxPodName +// } func waitForVaultUnseal(dryRun bool, config *configs.Config) { if dryRun { @@ -99,7 +100,7 @@ func createVaultConfiguredSecret(dryRun bool, config *configs.Config) { if err != nil { log.Panicf("failed to create secret for vault-configured: %s", err) } - log.Println("the secret create output is: %s", output.String()) + log.Printf("the secret create output is: %s", output.String()) viper.Set("vault.configuredsecret", true) viper.WriteConfig() @@ -136,6 +137,15 @@ func getSecretValue(k8sClient coreV1Types.SecretInterface, secretName, key strin return string(secret.Data[key]) } +func patchSecret(k8sClient coreV1Types.SecretInterface, secretName, key, val string) { + secret, err := k8sClient.Get(context.TODO(), secretName, metaV1.GetOptions{}) + if err != nil { + log.Println(fmt.Sprintf("error getting key: %s from secret: %s", key, secretName), err) + } + secret.Data[key] = []byte(val) + k8sClient.Update(context.TODO(), secret, metaV1.UpdateOptions{}) +} + func waitForNamespaceandPods(dryRun bool, config *configs.Config, namespace, podLabel string) { if dryRun { log.Printf("[#99] Dry-run mode, waitForNamespaceandPods skipped") diff --git a/internal/argocd/argocd.go b/internal/argocd/argocd.go index c940b8c81..8cdf1a9f0 100644 --- a/internal/argocd/argocd.go +++ b/internal/argocd/argocd.go @@ -145,20 +145,26 @@ func GetArgocdAuthToken(dryRun bool) string { x := 3 for i := 0; i < x; i++ { + log.Print("requesting auth token from argocd: attempt %s of %s", i, x) + time.Sleep(1 * time.Second) res, err := client.Do(req) + if err != nil { - log.Panic("error requesting auth token from argocd", err) - } else { - defer res.Body.Close() + log.Print("error requesting auth token from argocd", err) + continue + } else { + defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { - log.Panic("error sending POST request to get argocd auth token :", err) + log.Print("error sending POST request to get argocd auth token:", err) + continue } var dat map[string]interface{} if err := json.Unmarshal(body, &dat); err != nil { - log.Panicf("error unmarshalling %s", err) + log.Print("error unmarshalling %s", err) + continue } token := dat["token"] viper.Set("argocd.admin.apitoken", token) @@ -168,6 +174,9 @@ func GetArgocdAuthToken(dryRun bool) string { return token.(string) } } + log.Panic("Fail to get a token") + // This code is unreacheble, as in absence of token we want to fail the install. + // I kept is to avoid compiler to complain. return "" } @@ -188,3 +197,21 @@ func SyncArgocdApplication(dryRun bool, applicationName, argocdAuthToken string) log.Panicf("error: curl appSync failed failed %s", err) } } + +func DeleteArgocdApplicationNoCascade(dryRun bool, applicationName, argocdAuthToken string) { + if dryRun { + log.Printf("[#99] Dry-run mode, SyncArgocdApplication skipped.") + return + } + + // todo need to replace this with a curl wrapper and see if it WORKS + + url := fmt.Sprintf("https://localhost:8080/api/v1/applications/%s?cascade=false", applicationName) + var outb bytes.Buffer + + _, _, err := pkg.ExecShellReturnStrings("curl", "-k", "-L", "-X", "DELETE", url, "-H", fmt.Sprintf("Authorization: Bearer %s", argocdAuthToken)) + log.Println("the value from the curl command to delete registry in argocd is:", outb.String()) + if err != nil { + log.Panicf("error: curl app delete failed %s", err) + } +} diff --git a/internal/gitlab/gitlab.go b/internal/gitlab/gitlab.go index b0d88b697..664e91233 100644 --- a/internal/gitlab/gitlab.go +++ b/internal/gitlab/gitlab.go @@ -124,7 +124,7 @@ func PushGitOpsToGitLab(dryRun bool) { }, }) if err != nil { - log.Panicf("error committing changes", err) + log.Panicf("error committing changes %s", err) } log.Println("setting auth...") @@ -141,34 +141,38 @@ func PushGitOpsToGitLab(dryRun bool) { Auth: auth, }) if err != nil { - log.Panicf("error pushing to remote", err) + log.Panicf("error pushing to remote %s", err) } } -func AwaitGitlab(dryRun bool) { +func AwaitHost(appName string, dryRun bool) { - log.Println("AwaitGitlab called") + log.Println("AwaitHost called") if dryRun { - log.Printf("[#99] Dry-run mode, AwaitGitlab skipped.") + log.Printf("[#99] Dry-run mode, AwaitHost skipped.") return } max := 200 for i := 0; i < max; i++ { hostedZoneName := viper.GetString("aws.hostedzonename") - resp, _ := http.Get(fmt.Sprintf("https://gitlab.%s", hostedZoneName)) + resp, _ := http.Get(fmt.Sprintf("https://%s.%s", appName, hostedZoneName)) if resp != nil && resp.StatusCode == 200 { - log.Println("gitlab host resolved, 30 second grace period required...") + log.Println(fmt.Printf("%s host resolved, 30 second grace period required...", appName)) time.Sleep(time.Second * 30) i = max } else { - log.Println("gitlab host not resolved, sleeping 10s") + log.Println(fmt.Printf("%s host not resolved, sleeping 10s", appName)) time.Sleep(time.Second * 10) } } } func ProduceGitlabTokens(dryRun bool) { + if dryRun { + log.Printf("[#99] Dry-run mode, ProduceGitlabTokens skipped.") + return + } //TODO: Should this step be skipped if already executed? config := configs.ReadConfig() k8sConfig, err := clientcmd.BuildConfigFromFlags("", config.KubeConfigPath) @@ -180,10 +184,6 @@ func ProduceGitlabTokens(dryRun bool) { log.Panic(err.Error()) } log.Println("discovering gitlab toolbox pod") - if dryRun { - log.Printf("[#99] Dry-run mode, ProduceGitlabTokens skipped.") - return - } time.Sleep(30 * time.Second) // todo: move it to config k8s.ArgocdSecretClient = clientset.CoreV1().Secrets("argocd") @@ -195,8 +195,8 @@ func ProduceGitlabTokens(dryRun bool) { log.Println("discovering gitlab toolbox pod") - k8s.GitlabPodsClient = clientset.CoreV1().Pods("gitlab") - gitlabPodName := k8s.GetPodNameByLabel(k8s.GitlabPodsClient, "toolbox") + gitlabPodClient := clientset.CoreV1().Pods("gitlab") + gitlabPodName := k8s.GetPodNameByLabel(gitlabPodClient, "app=toolbox") k8s.GitlabSecretClient = clientset.CoreV1().Secrets("gitlab") secrets, err := k8s.GitlabSecretClient.List(context.TODO(), metaV1.ListOptions{}) @@ -391,27 +391,30 @@ func ChangeRegistryToGitLab(dryRun bool) { var secrets bytes.Buffer c, err := template.New("creds-gitlab").Parse(` - apiVersion: v1 - data: - password: {{ .PersonalAccessToken }} - url: {{ .URL }} - username: cm9vdA== - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repo-creds - name: creds-gitlab - namespace: argocd - type: Opaque - `) + apiVersion: v1 + data: + password: {{ .PersonalAccessToken }} + url: {{ .URL }} + username: cm9vdA== + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repo-creds + name: creds-gitlab + namespace: argocd + type: Opaque + `) if err := c.Execute(&secrets, creds); err != nil { log.Panicf("error executing golang template for git repository credentials template %s", err) } ba := []byte(secrets.String()) err = yaml.Unmarshal(ba, &argocdRepositoryAccessTokenSecret) + if err != nil { + log.Println("error unmarshalling yaml during argocd repository secret create", err) + } _, err = k8s.ArgocdSecretClient.Create(context.TODO(), argocdRepositoryAccessTokenSecret, metaV1.CreateOptions{}) if err != nil { @@ -421,21 +424,21 @@ func ChangeRegistryToGitLab(dryRun bool) { var repoSecrets bytes.Buffer c, err = template.New("repo-gitlab").Parse(` - apiVersion: v1 - data: - project: ZGVmYXVsdA== - type: Z2l0 - url: {{ .FullURL }} - kind: Secret - metadata: - annotations: - managed-by: argocd.argoproj.io - labels: - argocd.argoproj.io/secret-type: repository - name: repo-gitlab - namespace: argocd - type: Opaque - `) + apiVersion: v1 + data: + project: ZGVmYXVsdA== + type: Z2l0 + url: {{ .FullURL }} + kind: Secret + metadata: + annotations: + managed-by: argocd.argoproj.io + labels: + argocd.argoproj.io/secret-type: repository + name: repo-gitlab + namespace: argocd + type: Opaque + `) if err := c.Execute(&repoSecrets, creds); err != nil { log.Panicf("error executing golang template for gitops repository template %s", err) } @@ -448,6 +451,10 @@ func ChangeRegistryToGitLab(dryRun bool) { log.Panicf("error creating argocd repository connection secret %s", err) } + // curl -X 'DELETE' \ + // 'https://$ARGO_ADDRESS/api/v1/applications/registry?cascade=false' \ + // -H 'accept: application/json' + _, _, err = pkg.ExecShellReturnStrings(config.KubectlClientPath, "--kubeconfig", config.KubeConfigPath, "-n", "argocd", "apply", "-f", fmt.Sprintf("%s/gitops/components/gitlab/argocd-adopts-gitlab.yaml", config.K1FolderPath)) if err != nil { log.Panicf("failed to call execute kubectl apply of argocd patch to adopt gitlab: %s", err) @@ -538,6 +545,8 @@ func PushGitRepo(dryRun bool, config *configs.Config, gitOrigin, repoName string os.RemoveAll(repoDir + "/terraform/gitlab/.terraform") os.RemoveAll(repoDir + "/terraform/vault/.terraform") os.Remove(repoDir + "/terraform/base/.terraform.lock.hcl") + os.Remove(repoDir + "/terraform/vault/.terraform.lock.hcl") + os.Remove(repoDir + "/terraform/users/.terraform.lock.hcl") os.Remove(repoDir + "/terraform/gitlab/.terraform.lock.hcl") CommitToRepo(repo, repoName) auth, _ := pkg.PublicKey() @@ -555,12 +564,53 @@ func PushGitRepo(dryRun bool, config *configs.Config, gitOrigin, repoName string } if gitOrigin == "gitlab" { + registryFileContent := `apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: argocd-components + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "100" +spec: + project: default + source: + repoURL: ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops + path: components/argocd + targetRevision: HEAD + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + retry: + limit: 5 + backoff: + duration: 5s + maxDuration: 5m0s + factor: 2` + file, err := os.Create(fmt.Sprintf("%s/gitops/registry/argocd.yaml", config.K1FolderPath)) + if err != nil { + log.Println(err) + } + _, err = file.WriteString(registryFileContent) + if err != nil { + log.Println(err) + } + file.Close() + pkg.Detokenize(repoDir) os.RemoveAll(repoDir + "/terraform/base/.terraform") os.RemoveAll(repoDir + "/terraform/gitlab/.terraform") os.RemoveAll(repoDir + "/terraform/vault/.terraform") os.Remove(repoDir + "/terraform/base/.terraform.lock.hcl") + os.Remove(repoDir + "/terraform/vault/.terraform.lock.hcl") + os.Remove(repoDir + "/terraform/users/.terraform.lock.hcl") os.Remove(repoDir + "/terraform/gitlab/.terraform.lock.hcl") + CommitToRepo(repo, repoName) auth := &gitHttp.BasicAuth{ Username: "root", diff --git a/internal/k8s/kubernetes.go b/internal/k8s/kubernetes.go index f3f5c77b9..fc395aa9b 100644 --- a/internal/k8s/kubernetes.go +++ b/internal/k8s/kubernetes.go @@ -8,13 +8,14 @@ import ( "context" "encoding/json" "fmt" + "log" + "time" + "github.com/kubefirst/kubefirst/internal/argocd" "github.com/kubefirst/kubefirst/pkg" "github.com/spf13/viper" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" coreV1Types "k8s.io/client-go/kubernetes/typed/core/v1" - "log" - "time" ) var vaultRootToken string @@ -24,12 +25,13 @@ var gitlabToolboxPodName string var GitlabSecretClient coreV1Types.SecretInterface var VaultSecretClient coreV1Types.SecretInterface var ArgocdSecretClient coreV1Types.SecretInterface -var GitlabPodsClient coreV1Types.PodInterface -func GetPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) string { - pods, err := gitlabPodsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", label)}) +// var GitlabPodsClient coreV1Types.PodInterface + +func GetPodNameByLabel(podsClient coreV1Types.PodInterface, label string) string { + pods, err := podsClient.List(context.TODO(), metaV1.ListOptions{LabelSelector: label}) if err != nil { - fmt.Println(err) + log.Println(err) } gitlabToolboxPodName = pods.Items[0].Name @@ -37,6 +39,21 @@ func GetPodNameByLabel(gitlabPodsClient coreV1Types.PodInterface, label string) return gitlabToolboxPodName } +func DeletePodByName(podsClient coreV1Types.PodInterface, podName string) { + err := podsClient.Delete(context.TODO(), podName, metaV1.DeleteOptions{}) + if err != nil { + log.Println(err) + } +} + +// func CreateRepoSecret() { + +// } + +// func CreateCredentialsTemplateSecret() { + +// } + func getVaultRootToken(vaultSecretClient coreV1Types.SecretInterface) string { name := "vault-unseal-keys" log.Printf("Reading secret %s\n", name) diff --git a/internal/vault/vault.go b/internal/vault/vault.go index 8cdd677b6..510761af5 100644 --- a/internal/vault/vault.go +++ b/internal/vault/vault.go @@ -187,7 +187,7 @@ func AddGitlabOidcApplications(dryRun bool) { func addVaultSecret(secretPath string, secretData map[string]interface{}) { config := vault.DefaultConfig() - config.Address = fmt.Sprintf("https://vault.%s", viper.GetString("aws.hostedzonename")) + config.Address = viper.GetString("vault.local.service") client, err := vault.NewClient(config) if err != nil { diff --git a/pkg/helpers.go b/pkg/helpers.go index 762b7703c..a5b8dfd3b 100644 --- a/pkg/helpers.go +++ b/pkg/helpers.go @@ -3,13 +3,14 @@ package pkg import ( "errors" "fmt" - "github.com/kubefirst/kubefirst/configs" "io/ioutil" "log" "os" "path/filepath" "strings" + "github.com/kubefirst/kubefirst/configs" + "github.com/spf13/viper" ) @@ -30,7 +31,7 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { return nil // } - if strings.Contains(path, ".gitClient") || strings.Contains(path, ".terraform") { + if strings.Contains(path, ".gitClient") || strings.Contains(path, ".terraform") || strings.Contains(path, ".git/") { return nil } @@ -68,6 +69,7 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { awsAccountId := viper.GetString("aws.accountid") kmsKeyId := viper.GetString("vault.kmskeyid") clusterName := viper.GetString("cluster-name") + argocdOidcClientId := viper.GetString(("gitlab.oidc.argocd.applicationid")) newContents = strings.Replace(newContents, "", strings.TrimSpace(botPublicKey), -1) newContents = strings.Replace(newContents, "", bucketStateStore, -1) @@ -84,6 +86,10 @@ func DetokenizeDirectory(path string, fi os.FileInfo, err error) error { } newContents = strings.Replace(newContents, "", clusterName, -1) + if argocdOidcClientId != "" { + newContents = strings.Replace(newContents, "", argocdOidcClientId, -1) + } + if viper.GetBool("create.terraformapplied.gitlab") { newContents = strings.Replace(newContents, "", hostedZoneName, -1) newContents = strings.Replace(newContents, "", region, -1) diff --git a/pkg/keys.go b/pkg/keys.go index 21c4caf46..3dbae86aa 100644 --- a/pkg/keys.go +++ b/pkg/keys.go @@ -32,34 +32,6 @@ func CreateSshKeyPair() { privateKey := viper.GetString("botprivatekey") var argocdInitValuesYaml = []byte(fmt.Sprintf(` -server: - additionalApplications: - - name: registry - namespace: argocd - additionalLabels: {} - additionalAnnotations: {} - finalizers: - - resources-finalizer.argocd.argoproj.io - project: default - source: - repoURL: ssh://soft-serve.soft-serve.svc.cluster.local:22/gitops - targetRevision: HEAD - path: registry - destination: - server: https://kubernetes.default.svc - namespace: argocd - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true - retry: - limit: 5 - backoff: - duration: 5s - maxDuration: 5m0s - factor: 2 configs: repositories: soft-serve-gitops: