From 58609b2fb856488c9f69e641d24d7bc087dd12be Mon Sep 17 00:00:00 2001 From: Benjamin Gervan Date: Mon, 6 Nov 2023 10:46:24 +0100 Subject: [PATCH 1/6] Create install config and logic to skip prompts --- cmd/kuberoCli/install.go | 416 +++++++++++++++++++++++---------- cmd/kuberoCli/install.types.go | 67 ++++++ cmd/kuberoCli/root.go | 2 +- go.mod | 3 +- go.sum | 2 + 5 files changed, 370 insertions(+), 120 deletions(-) diff --git a/cmd/kuberoCli/install.go b/cmd/kuberoCli/install.go index 0628122..b581512 100644 --- a/cmd/kuberoCli/install.go +++ b/cmd/kuberoCli/install.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/base64" "fmt" + "github.com/creasty/defaults" + "k8s.io/utils/strings/slices" "log" "math/rand" "os" @@ -23,6 +25,8 @@ import ( "k8s.io/client-go/tools/clientcmd" ) +var kuberoConfig KuberoConfig + // installCmd represents the install command var installCmd = &cobra.Command{ Use: "install", @@ -37,8 +41,30 @@ required binaries: rand.Seed(time.Now().UnixNano()) + if err := defaults.Set(&kuberoConfig); err != nil { + panic(err) + } + + if arg_generate_config { + writeInstallCLIConfig() + return + } + checkAllBinaries() + // Check config file + if arg_config != "" { + // Read yaml + yamlFile, err := os.ReadFile(arg_config) + if err != nil { + log.Fatal(err) + } + yaml.Unmarshal(yamlFile, &kuberoConfig) + kuberoConfig.configLoaded = true + clusterType = kuberoConfig.ClusterType + cfmt.Println("{{✓ Kubero install config loaded}}::lightGreen") + } + switch arg_component { case "metrics": installMetrics() @@ -89,6 +115,8 @@ var arg_apiToken string var arg_port string var arg_portSecure string var clusterType string +var arg_config string +var arg_generate_config bool var arg_component string var install_olm bool var ingressControllerVersion = "v1.7.0" // https://github.com/kubernetes/ingress-nginx/tags -> controller-v1.5.1 @@ -97,6 +125,8 @@ var ingressControllerVersion = "v1.7.0" // https://github.com/kubernetes/ingress var clusterTypeList = []string{"kind", "linode", "scaleway", "gke", "digitalocean"} func init() { + installCmd.Flags().StringVar(&arg_config, "config", "", "config file yaml to propmtless install kubero") + installCmd.Flags().BoolVar(&arg_generate_config, "generate-config", false, "generate config file yaml to propmtless install kubero") installCmd.Flags().StringVarP(&arg_component, "component", "c", "", "install component (kubernetes,olm,ingress,metrics,certmanager,kubero-operator,kubero-ui)") installCmd.Flags().StringVarP(&arg_adminUser, "user", "u", "", "Admin username for the kubero UI") installCmd.Flags().StringVarP(&arg_adminPassword, "user-password", "U", "", "Password for the admin user") @@ -109,6 +139,19 @@ func init() { install_olm = false } +func writeInstallCLIConfig() { + if arg_generate_config { + // Write config file + kuberoConfigYaml, _ := yaml.Marshal(kuberoConfig) + kuberoConfigErr := os.WriteFile("kuberoInstallConfigSample.yaml", kuberoConfigYaml, 0644) + if kuberoConfigErr != nil { + fmt.Println(kuberoConfigErr) + return + } + cfmt.Println("{{✓ Kubero install config file generated to kuberoInstallConfigSample.yaml - make a copy to avoid being overwritten}}::lightGreen") + } +} + func checkAllBinaries() { cfmt.Println("\n Check for required binaries") if !checkBinary("kubectl") { @@ -151,12 +194,18 @@ func checkBinary(binary string) bool { } func installKubernetes() { - kubernetesInstall := promptLine("1) Create a kubernetes cluster", "[y,n]", "y") - if kubernetesInstall != "y" { - return - } + if kuberoConfig.configLoaded { + if !kuberoConfig.Install.Kubernetes { + return + } + } else { + kubernetesInstall := promptLine("1) Create a kubernetes cluster", "[y,n]", "y") + if kubernetesInstall != "y" { + return + } - clusterType = selectFromList("Select a Kubernetes provider", clusterTypeList, "") + clusterType = selectFromList("Select a Kubernetes provider", clusterTypeList, "") + } switch clusterType { case "scaleway": @@ -232,6 +281,10 @@ func checkCluster() { out, _ := exec.Command("kubectl", "config", "get-contexts").Output() fmt.Println(string(out)) + if kuberoConfig.configLoaded { + return + } + clusterselect := promptLine("Is the CURRENT cluster the one you wish to install Kubero?", "[y,n]", "y") if clusterselect == "n" { os.Exit(0) @@ -242,7 +295,7 @@ func installOLM() { openshiftInstalled, _ := exec.Command("kubectl", "get", "deployment", "olm-operator", "-n", "openshift-operator-lifecycle-manager").Output() if len(openshiftInstalled) > 0 { - cfmt.Println("{{✓ OLM is allredy installed}}::lightGreen") + cfmt.Println("{{✓ OLM is already installed}}::lightGreen") return } @@ -250,26 +303,40 @@ func installOLM() { namespace := "olm" olmInstalled, _ := exec.Command("kubectl", "get", "deployment", "olm-operator", "-n", namespace).Output() if len(olmInstalled) > 0 { - cfmt.Println("{{✓ OLM is allredy installed}}::lightGreen") + cfmt.Println("{{✓ OLM is already installed}}::lightGreen") return } - olmInstall := promptLine("2) Install OLM", "[y,n]", "n") - if olmInstall != "y" { - install_olm = false - return + olmRelease := "0.23.1" + + if kuberoConfig.configLoaded { + install_olm = kuberoConfig.Install.OLM + if !install_olm { + return + } + + if kuberoConfig.OLMRelease != "" { + olmRelease = kuberoConfig.OLMRelease + } } else { - install_olm = true + olmInstall := promptLine("2) Install OLM", "[y,n]", "n") + if olmInstall != "y" { + install_olm = false + return + } else { + install_olm = true + } + + olmRelease = promptLine("Install OLM from which release?", "[0.20.0,0.21.0,0.22.0,0.23.1]", "0.23.1") } - olmRelease := promptLine("Install OLM from which release?", "[0.20.0,0.21.0,0.22.0,0.23.1]", "0.23.1") olmURL := "https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v" + olmRelease olmSpinner := spinner.New("Install OLM") olmCRDInstalled, _ := exec.Command("kubectl", "get", "crd", "subscriptions.operators.coreos.com").Output() if len(olmCRDInstalled) > 0 { - cfmt.Println("{{✓ OLM CRD's allredy installed}}::lightGreen") + cfmt.Println("{{✓ OLM CRD's already installed}}::lightGreen") } else { olmSpinner.Start("run command : kubectl create -f " + olmURL + "/olm.yaml") _, olmCRDErr := exec.Command("kubectl", "create", "-f", olmURL+"/crds.yaml").Output() @@ -315,12 +382,18 @@ func installMetrics() { installed, _ := exec.Command("kubectl", "get", "deployments.apps", "metrics-server", "-n", "kube-system").Output() if len(installed) > 0 { - cfmt.Println("{{✓ Metrics is allredy enabled}}::lightGreen") + cfmt.Println("{{✓ Metrics is already enabled}}::lightGreen") return } - install := promptLine("4) Install Kubernetes internal metrics service (required for HPA and stats)", "[y,n]", "y") - if install != "y" { - return + if kuberoConfig.configLoaded { + if !kuberoConfig.Install.Metrics { + return + } + } else { + install := promptLine("4) Install Kubernetes internal metrics service (required for HPA and stats)", "[y,n]", "y") + if install != "y" { + return + } } //components := "https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml" @@ -335,15 +408,24 @@ func installMetrics() { } func installIngress() { + ingressReadyLabel, _ := exec.Command("kubectl", "get", "nodes", "--selector=ingress-ready=true", "-o", "jsonpath='{.items[*].metadata.name}'").Output() + if len(ingressReadyLabel) == 0 { + cfmt.Println("{{✗ Ingress: no nodes with label ingress-ready=true found}}::red") + } ingressInstalled, _ := exec.Command("kubectl", "get", "ns", "ingress-nginx").Output() if len(ingressInstalled) > 0 { - cfmt.Println("{{✓ Ingress is allredy installed}}::lightGreen") + cfmt.Println("{{✓ Ingress is already installed}}::lightGreen") return } - ingressInstall := promptLine("3) Install Ingress", "[y,n]", "y") - if ingressInstall != "y" { + ingressInstall := true + if kuberoConfig.configLoaded { + ingressInstall = kuberoConfig.Install.Ingress + } else { + ingressInstall = promptLine("3) Install Ingress", "[y,n]", "y") == "y" + } + if !ingressInstall { return } else { @@ -364,9 +446,13 @@ func installIngress() { case "digitalocean": prefill = "do" } - - ingressProviderList := []string{"kind", "aws", "baremetal", "cloud", "do", "exoscale", "scw"} - ingressProvider := selectFromList("Provider [kind, aws, baremetal, cloud(Azure,Google,Oracle,Linode), do(digital ocean), exoscale, scw(scaleway)]", ingressProviderList, prefill) + ingressProvider := prefill + if kuberoConfig.configLoaded { + ingressProvider = kuberoConfig.IngressProvider + } else { + ingressProviderList := []string{"kind", "aws", "baremetal", "cloud", "do", "exoscale", "scw"} + ingressProvider = selectFromList("Provider [kind, aws, baremetal, cloud(Azure,Google,Oracle,Linode), do(digital ocean), exoscale, scw(scaleway)]", ingressProviderList, prefill) + } ingressSpinner := spinner.New("Install Ingress") URL := "https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-" + ingressControllerVersion + "/deploy/static/provider/" + ingressProvider + "/deploy.yaml" @@ -387,7 +473,7 @@ func installKuberoOperator() { kuberoInstalled, _ := exec.Command("kubectl", "get", "operator", "kubero-operator.operators").Output() if len(kuberoInstalled) > 0 { - cfmt.Println("{{✓ Kubero Operator is allredy installed}}::lightGreen") + cfmt.Println("{{✓ Kubero Operator is already installed}}::lightGreen") return } @@ -445,9 +531,13 @@ func installKuberoOperatorSlim() { } func installKuberoUi() { - - ingressInstall := promptLine("7) Install Kubero UI", "[y,n]", "y") - if ingressInstall != "y" { + kuberoUIInstall := true + if kuberoConfig.configLoaded { + kuberoUIInstall = kuberoConfig.Install.KuberoUI + } else { + kuberoUIInstall = promptLine("7) Install Kubero UI", "[y,n]", "y") == "y" + } + if !kuberoUIInstall { return } @@ -468,21 +558,33 @@ func installKuberoUi() { if len(kuberoSecretInstalled) > 0 { cfmt.Println("{{✓ Kubero Secret exists}}::lightGreen") } else { + webhookSecret := generateRandomString(20, "") + sessionKey := generateRandomString(20, "") + + if kuberoConfig.configLoaded { + webhookSecret = kuberoConfig.KuberoUI.WebhookSecret + sessionKey = kuberoConfig.KuberoUI.SessionKey + arg_adminUser = "admin" + arg_adminPassword = generateRandomString(12, "") + arg_apiToken = generateRandomString(20, "") + if kuberoConfig.KuberoUI.AdminUser != "" { + arg_adminUser = kuberoConfig.KuberoUI.AdminUser + } + } else { + webhookSecret = promptLine("Random string for your webhook secret", "", webhookSecret) + sessionKey = promptLine("Random string for your session key", "", sessionKey) - webhookSecret := promptLine("Random string for your webhook secret", "", generateRandomString(20, "")) - - sessionKey := promptLine("Random string for your session key", "", generateRandomString(20, "")) - - if arg_adminUser == "" { - arg_adminUser = promptLine("Admin User", "", "admin") - } + if arg_adminUser == "" { + arg_adminUser = promptLine("Admin User", "", "admin") + } - if arg_adminPassword == "" { - arg_adminPassword = promptLine("Admin Password", "", generateRandomString(12, "")) - } + if arg_adminPassword == "" { + arg_adminPassword = promptLine("Admin Password", "", generateRandomString(12, "")) + } - if arg_apiToken == "" { - arg_apiToken = promptLine("Random string for admin API token", "", generateRandomString(20, "")) + if arg_apiToken == "" { + arg_apiToken = promptLine("Random string for admin API token", "", generateRandomString(20, "")) + } } var userDB []User @@ -496,51 +598,73 @@ func installKuberoUi() { "--from-literal=KUBERO_USERS="+userDBencoded, ) - githubConfigure := promptLine("Configure Github", "[y,n]", "y") - githubPersonalAccessToken := "" - if githubConfigure == "y" { - githubPersonalAccessToken = promptLine("Github personal access token", "", "") - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITHUB_PERSONAL_ACCESS_TOKEN="+githubPersonalAccessToken) - } - - giteaConfigure := promptLine("Configure Gitea", "[y,n]", "n") - giteaPersonalAccessToken := "" - giteaBaseUrl := "" - if giteaConfigure == "y" { - giteaPersonalAccessToken = promptLine("Gitea personal access token", "", "") - giteaBaseUrl = promptLine("Gitea URL", "http://localhost:3000", "") - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITEA_PERSONAL_ACCESS_TOKEN="+giteaPersonalAccessToken) - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITEA_BASEURL="+giteaBaseUrl) - } - - gogsConfigure := promptLine("Configure Gogs", "[y,n]", "n") - gogsPersonalAccessToken := "" - gogsBaseUrl := "" - if gogsConfigure == "y" { - gogsPersonalAccessToken = promptLine("Gogs personal access token", "", "") - gogsBaseUrl = promptLine("Gogs URL", "http://localhost:3000", "") - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GOGS_PERSONAL_ACCESS_TOKEN="+gogsPersonalAccessToken) - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GOGS_BASEURL="+gogsBaseUrl) - } - - gitlabConfigure := promptLine("Configure Gitlab", "[y,n]", "n") - gitlabPersonalAccessToken := "" - gitlabBaseUrl := "" - if gitlabConfigure == "y" { - gitlabPersonalAccessToken = promptLine("Gitlab personal access token", "", "") - gitlabBaseUrl = promptLine("Gitlab URL", "http://localhost:3080", "") - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITLAB_PERSONAL_ACCESS_TOKEN="+gitlabPersonalAccessToken) - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITLAB_BASEURL="+gitlabBaseUrl) - } - - bitbucketConfigure := promptLine("Configure Bitbucket", "[y,n]", "n") - bitbucketUsername := "" - bitbucketAppPassword := "" - if bitbucketConfigure == "y" { - bitbucketUsername = promptLine("Bitbucket Username", "", "") - bitbucketAppPassword = promptLine("Bitbucket App Password", "", "") - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=BITBUCKET_USERNAME="+bitbucketUsername) - createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=BITBUCKET_APP_PASSWORD="+bitbucketAppPassword) + if kuberoConfig.configLoaded { + if kuberoConfig.KuberoUI.Github.Enabled { + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITHUB_PERSONAL_ACCESS_TOKEN="+kuberoConfig.KuberoUI.Github.PersonalAccessToken) + } + if kuberoConfig.KuberoUI.Gitea.Enabled { + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITEA_PERSONAL_ACCESS_TOKEN="+kuberoConfig.KuberoUI.Gitea.PersonalAccessToken) + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITEA_BASEURL="+kuberoConfig.KuberoUI.Gitea.BaseURL) + } + if kuberoConfig.KuberoUI.Gogs.Enabled { + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GOGS_PERSONAL_ACCESS_TOKEN="+kuberoConfig.KuberoUI.Gogs.PersonalAccessToken) + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GOGS_BASEURL="+kuberoConfig.KuberoUI.Gogs.BaseURL) + } + if kuberoConfig.KuberoUI.Gitlab.Enabled { + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITLAB_PERSONAL_ACCESS_TOKEN="+kuberoConfig.KuberoUI.Gitlab.PersonalAccessToken) + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITLAB_BASEURL="+kuberoConfig.KuberoUI.Gitlab.BaseURL) + } + if kuberoConfig.KuberoUI.Bitbucket.Enabled { + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=BITBUCKET_USERNAME="+kuberoConfig.KuberoUI.Bitbucket.Username) + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=BITBUCKET_APP_PASSWORD="+kuberoConfig.KuberoUI.Bitbucket.AppPassword) + } + } else { + githubConfigure := promptLine("Configure Github", "[y,n]", "y") + githubPersonalAccessToken := "" + if githubConfigure == "y" { + githubPersonalAccessToken = promptLine("Github personal access token", "", "") + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITHUB_PERSONAL_ACCESS_TOKEN="+githubPersonalAccessToken) + } + + giteaConfigure := promptLine("Configure Gitea", "[y,n]", "n") + giteaPersonalAccessToken := "" + giteaBaseUrl := "" + if giteaConfigure == "y" { + giteaPersonalAccessToken = promptLine("Gitea personal access token", "", "") + giteaBaseUrl = promptLine("Gitea URL", "http://localhost:3000", "") + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITEA_PERSONAL_ACCESS_TOKEN="+giteaPersonalAccessToken) + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITEA_BASEURL="+giteaBaseUrl) + } + + gogsConfigure := promptLine("Configure Gogs", "[y,n]", "n") + gogsPersonalAccessToken := "" + gogsBaseUrl := "" + if gogsConfigure == "y" { + gogsPersonalAccessToken = promptLine("Gogs personal access token", "", "") + gogsBaseUrl = promptLine("Gogs URL", "http://localhost:3000", "") + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GOGS_PERSONAL_ACCESS_TOKEN="+gogsPersonalAccessToken) + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GOGS_BASEURL="+gogsBaseUrl) + } + + gitlabConfigure := promptLine("Configure Gitlab", "[y,n]", "n") + gitlabPersonalAccessToken := "" + gitlabBaseUrl := "" + if gitlabConfigure == "y" { + gitlabPersonalAccessToken = promptLine("Gitlab personal access token", "", "") + gitlabBaseUrl = promptLine("Gitlab URL", "http://localhost:3080", "") + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITLAB_PERSONAL_ACCESS_TOKEN="+gitlabPersonalAccessToken) + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITLAB_BASEURL="+gitlabBaseUrl) + } + + bitbucketConfigure := promptLine("Configure Bitbucket", "[y,n]", "n") + bitbucketUsername := "" + bitbucketAppPassword := "" + if bitbucketConfigure == "y" { + bitbucketUsername = promptLine("Bitbucket Username", "", "") + bitbucketAppPassword = promptLine("Bitbucket App Password", "", "") + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=BITBUCKET_USERNAME="+bitbucketUsername) + createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=BITBUCKET_APP_PASSWORD="+bitbucketAppPassword) + } } createSecretCommand.Args = append(createSecretCommand.Args, "-n", "kubero") @@ -567,16 +691,26 @@ func installKuberoUi() { var kuberoUIConfig KuberoUIConfig yaml.Unmarshal(kf.Body(), &kuberoUIConfig) - if arg_domain == "" { - arg_domain = promptLine("Kuberi UI Domain", "", "kubero.lacolhost.com") + webhookURL := "https://" + arg_domain + "/api/repo/webhooks" + kuberoUIssl := true + + if kuberoConfig.configLoaded { + arg_domain = kuberoConfig.KuberoUI.UI.Host + webhookURL = kuberoConfig.KuberoUI.UI.WebhookUrl + kuberoUIssl = kuberoConfig.KuberoUI.UI.SSL + } else { + if arg_domain == "" { + arg_domain = promptLine("Kuberi UI Domain", "", "kubero.localhost.com") + } + + webhookURL = promptLine("URL to which the webhooks should be sent", "", "https://"+arg_domain+"/api/repo/webhooks") + kuberoUIssl = promptLine("Enable SSL for the Kubero UI", "[y/n]", "y") == "y" } - kuberoUIConfig.Spec.Ingress.Hosts[0].Host = arg_domain - webhookURL := promptLine("URL to which the webhooks should be sent", "", "https://"+arg_domain+"/api/repo/webhooks") + kuberoUIConfig.Spec.Ingress.Hosts[0].Host = arg_domain kuberoUIConfig.Spec.Kubero.WebhookURL = webhookURL - kuberoUIssl := promptLine("Enable SSL for the Kubero UI", "[y/n]", "y") - if kuberoUIssl == "y" { + if kuberoUIssl { kuberoUIConfig.Spec.Ingress.Annotations.KubernetesIoIngressClass = "letsencrypt-prod" kuberoUIConfig.Spec.Ingress.Annotations.KubernetesIoTLSacme = "true" @@ -588,40 +722,67 @@ func installKuberoUi() { } } - kuberoUIRegistry := promptLine("Enable Buildpipeline for Kubero (BETA)", "[y/n]", "n") - if kuberoUIRegistry == "y" { + kuberoUIRegistry := false + if kuberoConfig.configLoaded { + kuberoUIRegistry = kuberoConfig.KuberoUI.Registry.Enabled + } else { + kuberoUIRegistry = promptLine("Enable Buildpipeline for Kubero (BETA)", "[y/n]", "n") == "y" + } + if kuberoUIRegistry { kuberoUIConfig.Spec.Registry.Enabled = true - kuberoUICreateRegistry := promptLine("Create a local Registry for Kubero", "[y/n]", "n") - if kuberoUICreateRegistry == "y" { + kuberoUICreateRegistry := false + kuberoUIRegistryPort := "443" + kuberoUIRegistryHost := "registry." + arg_domain + kuberoUIRegistryUsername := "admin" + kuberoUIRegistryPassword := generateRandomString(12, "") + kuberoUIRegistryStorage := "10Gi" + + storageClassList := getAvailableStorageClasses() + kuberoUIRegistryStorageClassName := "" + + if kuberoConfig.configLoaded { + kuberoUICreateRegistry = kuberoConfig.KuberoUI.Registry.Local + kuberoUIRegistryPort = kuberoConfig.KuberoUI.Registry.Port + if kuberoConfig.KuberoUI.Registry.Host != "" { + kuberoUIRegistryHost = kuberoConfig.KuberoUI.Registry.Host + } + kuberoUIRegistryUsername = kuberoConfig.KuberoUI.Registry.Username + kuberoUIRegistryPassword = kuberoConfig.KuberoUI.Registry.Password + kuberoUIRegistryStorage = kuberoConfig.KuberoUI.Registry.StorageSize + kuberoUIRegistryStorageClassName = kuberoConfig.KuberoUI.Registry.StorageClassName + + if !slices.Contains(storageClassList, kuberoUIRegistryStorageClassName) { + cfmt.Println("{{✗ The storage class " + kuberoUIRegistryStorageClassName + " is not available.}}::red") + return + } + } else { + kuberoUICreateRegistry = promptLine("Create a local Registry for Kubero", "[y/n]", "n") == "y" + kuberoUIRegistryPort = promptLine("Registry port", "", "443") + kuberoUIRegistryHost = promptLine("Registry domain", "", "registry."+arg_domain) + kuberoUIRegistryUsername = promptLine("Registry username", "", "admin") + kuberoUIRegistryPassword = promptLine("Registry password", "", generateRandomString(12, "")) + kuberoUIRegistryStorage = promptLine("Registry storage size", "", "10Gi") + kuberoUIRegistryStorageClassName = selectFromList("Registry storage class", storageClassList, "") + } + + if kuberoUICreateRegistry { kuberoUIConfig.Spec.Registry.Create = true } - kuberoUIRegistryPort := promptLine("Registry port", "", "443") kuberoUIConfig.Spec.Registry.Port, _ = strconv.Atoi(kuberoUIRegistryPort) - - kuberoUIRegistryHost := promptLine("Registry domain", "", "registry."+arg_domain) kuberoUIConfig.Spec.Registry.Host = kuberoUIRegistryHost - - kuberoUIRegistryUsername := promptLine("Registry username", "", "admin") kuberoUIConfig.Spec.Registry.Account.Username = kuberoUIRegistryUsername - - kuberoUIRegistryPassword := promptLine("Registry password", "", generateRandomString(12, "")) kuberoUIConfig.Spec.Registry.Account.Password = kuberoUIRegistryPassword kuberoUIRegistryPasswordBytes, _ := bcrypt.GenerateFromPassword([]byte(kuberoUIRegistryPassword), 14) kuberoUIConfig.Spec.Registry.Account.Hash = string(kuberoUIRegistryPasswordBytes) - kuberoUIRegistryStorage := promptLine("Registry storage size", "", "10Gi") kuberoUIConfig.Spec.Registry.Storage = kuberoUIRegistryStorage - - storageClassList := getAvailableStorageClasses() - - kuberoUIRegistryStorageClassName := selectFromList("Registry storage class", storageClassList, "") kuberoUIConfig.Spec.Registry.StorageClassName = kuberoUIRegistryStorageClassName } - if clusterType == "" { + if clusterType == "" && !kuberoConfig.configLoaded { clusterType = selectFromList("Which cluster type have you insalled?", clusterTypeList, "") } @@ -677,9 +838,13 @@ func installKuberoUi() { } func installCertManager() { - - install := promptLine("5) Install SSL Certmanager", "[y,n]", "y") - if install != "y" { + install := true + if kuberoConfig.configLoaded { + install = kuberoConfig.Install.CertManager + } else { + install = promptLine("5) Install SSL Certmanager", "[y,n]", "y") == "y" + } + if !install { return } @@ -731,8 +896,11 @@ func installCertManagerClusterissuer() { var certmanagerClusterIssuer CertmanagerClusterIssuer yaml.Unmarshal(kf.Body(), &certmanagerClusterIssuer) - arg_certmanagerContact := promptLine("Letsencrypt ACME contact email", "", "noreply@yourdomain.com") - certmanagerClusterIssuer.Spec.Acme.Email = arg_certmanagerContact + if kuberoConfig.configLoaded { + certmanagerClusterIssuer.Spec.Acme.Email = kuberoConfig.CertManager.AcmeEmail + } else { + certmanagerClusterIssuer.Spec.Acme.Email = promptLine("Letsencrypt ACME contact email", "", "noreply@yourdomain.com") + } certmanagerClusterIssuerYaml, _ := yaml.Marshal(certmanagerClusterIssuer) certmanagerClusterIssuerYamlErr := os.WriteFile("kuberoCertmanagerClusterIssuer.yaml", certmanagerClusterIssuerYaml, 0644) @@ -784,16 +952,28 @@ func installOLMCertManager() { func writeCLIconfig() { - ingressInstall := promptLine("8) Write the Kubero CLI config", "[y,n]", "y") - if ingressInstall != "y" { + writeCli := true + if kuberoConfig.configLoaded { + writeCli = kuberoConfig.Install.WriteCliConfig + } else { + writeCli = promptLine("8) Write the Kubero CLI config", "[y,n]", "y") == "y" + } + + if !writeCli { return } //TODO consider using SSL here. - url := promptLine("Kubero Host adress", "", "http://"+arg_domain+":"+arg_port) - viper.Set("api.url", url) + url := "http://" + arg_domain + ":" + arg_port + token := arg_apiToken - token := promptLine("Kubero Token", "", arg_apiToken) + if kuberoConfig.configLoaded { + // TODO: arg_port is not used ? + } else { + url = promptLine("Kubero Host address", "", "http://"+arg_domain+":"+arg_port) + token = promptLine("Kubero Token", "", arg_apiToken) + } + viper.Set("api.url", url) viper.Set("api.token", token) var config Config diff --git a/cmd/kuberoCli/install.types.go b/cmd/kuberoCli/install.types.go index 26b2984..8d14316 100644 --- a/cmd/kuberoCli/install.types.go +++ b/cmd/kuberoCli/install.types.go @@ -113,6 +113,73 @@ type KindConfig struct { } `yaml:"nodes"` } +type KuberoConfig struct { + configLoaded bool + ClusterType string `yaml:"clusterType" default:"kind"` + OLMRelease string `yaml:"OLMRelease" default:"0.23.1"` + IngressProvider string `yaml:"ingressProvider" default:"baremetal"` + Install struct { + Kubernetes bool `yaml:"kubernetes"` + OLM bool `yaml:"olm"` + Ingress bool `yaml:"ingress" default:"true"` + Metrics bool `yaml:"metrics"` + CertManager bool `yaml:"certManager"` + KuberoOperator bool `yaml:"kuberoOperator" default:"true"` + KuberoUI bool `yaml:"kuberoUI"` + Registry bool `yaml:"registry"` + WriteCliConfig bool `yaml:"writeCliConfig" default:"true"` + } `yaml:"install"` + CertManager struct { + AcmeEmail string `yaml:"acmeEmail" default:"noreply@yourdomain.com"` + } `yaml:"certManager"` + KuberoUI struct { + WebhookSecret string `yaml:"webhookSecret"` + SessionKey string `yaml:"sessionKey"` + AdminUser string `yaml:"adminUser" default:"admin"` + AdminPassword string `yaml:"adminPassword"` + ApiToken string `yaml:"apiToken"` + UI struct { + Host string `yaml:"host" default:"kubero.localhost.com"` + WebhookUrl string `yaml:"webhookUrl" default:"https://kubero.localhost.com/api/repo/webhooks"` + SSL bool `yaml:"ssl" default:"true"` + } `yaml:"ui"` + Registry struct { + Enabled bool `yaml:"enabled" default:"false"` + Local bool `yaml:"local" default:"false"` + Host string `yaml:"host"` + Port string `yaml:"port" default:"443"` + Username string `yaml:"username"` + Password string `yaml:"password"` + StorageSize string `yaml:"storageSize" default:"10Gi"` + StorageClassName string `yaml:"storageClassName" default:""` + } `yaml:"registry"` + Github struct { + Enabled bool `yaml:"enabled"` + PersonalAccessToken string `yaml:"personalAccessToken"` + } `yaml:"github"` + Gitea struct { + Enabled bool `yaml:"enabled"` + PersonalAccessToken string `yaml:"personalAccessToken"` + BaseURL string `yaml:"baseUrl"` + } `yaml:"gitea"` + Gogs struct { + Enabled bool `yaml:"enabled"` + PersonalAccessToken string `yaml:"personalAccessToken"` + BaseURL string `yaml:"baseUrl"` + } `yaml:"gogs"` + Gitlab struct { + Enabled bool `yaml:"enabled"` + PersonalAccessToken string `yaml:"personalAccessToken"` + BaseURL string `yaml:"baseUrl"` + } `yaml:"gitlab"` + Bitbucket struct { + Enabled bool `yaml:"enabled"` + Username string `yaml:"username"` + AppPassword string `yaml:"appPassword"` + } `yaml:"bitbucket"` + } `yaml:"kuberoUI"` +} + type KuberoUIConfig struct { APIVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` diff --git a/cmd/kuberoCli/root.go b/cmd/kuberoCli/root.go index 8578de8..3d247f6 100644 --- a/cmd/kuberoCli/root.go +++ b/cmd/kuberoCli/root.go @@ -290,7 +290,7 @@ func loadCLIConfig() { viper.UnmarshalKey("instances", &instanceList) - // iterate over all instances and and set the config path + // iterate over all instances and set the config path for instanceName, instance := range instanceList { instance.Name = instanceName instance.ConfigPath = viper.ConfigFileUsed() diff --git a/go.mod b/go.mod index 48dd3fb..1af5556 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module kubero go 1.19 require ( + github.com/creasty/defaults v1.7.0 github.com/go-git/go-billy/v5 v5.3.1 github.com/go-git/go-git/v5 v5.4.2 github.com/go-resty/resty/v2 v2.7.0 @@ -61,7 +62,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.1 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect + golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect diff --git a/go.sum b/go.sum index 1f60354..1035b61 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= +github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= 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= From 024caf2bd027a8fcca5bd1ec6309b44bc33c8051 Mon Sep 17 00:00:00 2001 From: Benjamin Gervan Date: Mon, 6 Nov 2023 10:52:34 +0100 Subject: [PATCH 2/6] Add TODO comment --- cmd/kuberoCli/install.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/kuberoCli/install.go b/cmd/kuberoCli/install.go index b581512..6435fd6 100644 --- a/cmd/kuberoCli/install.go +++ b/cmd/kuberoCli/install.go @@ -408,6 +408,7 @@ func installMetrics() { } func installIngress() { + // TODO: is this check correct? The ingress-nginx yaml contains this selector by default which blocks the install when no nodes have this label ingressReadyLabel, _ := exec.Command("kubectl", "get", "nodes", "--selector=ingress-ready=true", "-o", "jsonpath='{.items[*].metadata.name}'").Output() if len(ingressReadyLabel) == 0 { cfmt.Println("{{✗ Ingress: no nodes with label ingress-ready=true found}}::red") From 3a557c442ed2f30586cf54cd682db2af0c88b844 Mon Sep 17 00:00:00 2001 From: Benjamin Gervan Date: Mon, 6 Nov 2023 10:56:04 +0100 Subject: [PATCH 3/6] Stop when ingress-ready label is not exist --- cmd/kuberoCli/install.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kuberoCli/install.go b/cmd/kuberoCli/install.go index 6435fd6..57fde13 100644 --- a/cmd/kuberoCli/install.go +++ b/cmd/kuberoCli/install.go @@ -426,7 +426,7 @@ func installIngress() { } else { ingressInstall = promptLine("3) Install Ingress", "[y,n]", "y") == "y" } - if !ingressInstall { + if !ingressInstall || len(ingressReadyLabel) == 0 { return } else { From d1712c5b1fe55e0bbea1e542960f4377974fa2b8 Mon Sep 17 00:00:00 2001 From: Benjamin Gervan Date: Mon, 6 Nov 2023 11:32:16 +0100 Subject: [PATCH 4/6] Fix ingress label selector --- cmd/kuberoCli/install.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kuberoCli/install.go b/cmd/kuberoCli/install.go index 57fde13..f03c099 100644 --- a/cmd/kuberoCli/install.go +++ b/cmd/kuberoCli/install.go @@ -409,7 +409,7 @@ func installMetrics() { func installIngress() { // TODO: is this check correct? The ingress-nginx yaml contains this selector by default which blocks the install when no nodes have this label - ingressReadyLabel, _ := exec.Command("kubectl", "get", "nodes", "--selector=ingress-ready=true", "-o", "jsonpath='{.items[*].metadata.name}'").Output() + ingressReadyLabel, _ := exec.Command("kubectl", "get", "nodes", "--selector=ingress-nginx=true", "-o", "jsonpath='{.items[*].metadata.name}'").Output() if len(ingressReadyLabel) == 0 { cfmt.Println("{{✗ Ingress: no nodes with label ingress-ready=true found}}::red") } From ecf319084a8b58bc83fa84125a219f29816640da Mon Sep 17 00:00:00 2001 From: Benjamin Gervan Date: Mon, 6 Nov 2023 11:43:06 +0100 Subject: [PATCH 5/6] Revert label selector change and change defaults for random passwords --- cmd/kuberoCli/install.go | 16 +++++++++------- cmd/kuberoCli/install.types.go | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cmd/kuberoCli/install.go b/cmd/kuberoCli/install.go index f03c099..76589be 100644 --- a/cmd/kuberoCli/install.go +++ b/cmd/kuberoCli/install.go @@ -142,6 +142,11 @@ func init() { func writeInstallCLIConfig() { if arg_generate_config { // Write config file + kuberoConfig.KuberoUI.WebhookSecret = generateRandomString(20, "") + kuberoConfig.KuberoUI.SessionKey = generateRandomString(20, "") + kuberoConfig.KuberoUI.Registry.Password = generateRandomString(20, "") + kuberoConfig.KuberoUI.AdminPassword = generateRandomString(12, "") + kuberoConfig.KuberoUI.ApiToken = generateRandomString(20, "") kuberoConfigYaml, _ := yaml.Marshal(kuberoConfig) kuberoConfigErr := os.WriteFile("kuberoInstallConfigSample.yaml", kuberoConfigYaml, 0644) if kuberoConfigErr != nil { @@ -409,7 +414,7 @@ func installMetrics() { func installIngress() { // TODO: is this check correct? The ingress-nginx yaml contains this selector by default which blocks the install when no nodes have this label - ingressReadyLabel, _ := exec.Command("kubectl", "get", "nodes", "--selector=ingress-nginx=true", "-o", "jsonpath='{.items[*].metadata.name}'").Output() + ingressReadyLabel, _ := exec.Command("kubectl", "get", "nodes", "--selector=ingress-ready=true", "-o", "jsonpath='{.items[*].metadata.name}'").Output() if len(ingressReadyLabel) == 0 { cfmt.Println("{{✗ Ingress: no nodes with label ingress-ready=true found}}::red") } @@ -565,12 +570,9 @@ func installKuberoUi() { if kuberoConfig.configLoaded { webhookSecret = kuberoConfig.KuberoUI.WebhookSecret sessionKey = kuberoConfig.KuberoUI.SessionKey - arg_adminUser = "admin" - arg_adminPassword = generateRandomString(12, "") - arg_apiToken = generateRandomString(20, "") - if kuberoConfig.KuberoUI.AdminUser != "" { - arg_adminUser = kuberoConfig.KuberoUI.AdminUser - } + arg_adminUser = kuberoConfig.KuberoUI.AdminUser + arg_adminPassword = kuberoConfig.KuberoUI.AdminPassword + arg_apiToken = kuberoConfig.KuberoUI.ApiToken } else { webhookSecret = promptLine("Random string for your webhook secret", "", webhookSecret) sessionKey = promptLine("Random string for your session key", "", sessionKey) diff --git a/cmd/kuberoCli/install.types.go b/cmd/kuberoCli/install.types.go index 8d14316..de18cea 100644 --- a/cmd/kuberoCli/install.types.go +++ b/cmd/kuberoCli/install.types.go @@ -148,7 +148,7 @@ type KuberoConfig struct { Local bool `yaml:"local" default:"false"` Host string `yaml:"host"` Port string `yaml:"port" default:"443"` - Username string `yaml:"username"` + Username string `yaml:"username" default:"admin"` Password string `yaml:"password"` StorageSize string `yaml:"storageSize" default:"10Gi"` StorageClassName string `yaml:"storageClassName" default:""` From 07e7632185181cacb7677ced68652c6402b899e9 Mon Sep 17 00:00:00 2001 From: Benjamin Gervan Date: Mon, 6 Nov 2023 12:54:44 +0100 Subject: [PATCH 6/6] Typo fixes --- cmd/kuberoCli/install.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/kuberoCli/install.go b/cmd/kuberoCli/install.go index 76589be..d0614f9 100644 --- a/cmd/kuberoCli/install.go +++ b/cmd/kuberoCli/install.go @@ -343,7 +343,7 @@ func installOLM() { if len(olmCRDInstalled) > 0 { cfmt.Println("{{✓ OLM CRD's already installed}}::lightGreen") } else { - olmSpinner.Start("run command : kubectl create -f " + olmURL + "/olm.yaml") + olmSpinner.Start("run command : kubectl create -f " + olmURL + "/crds.yaml") _, olmCRDErr := exec.Command("kubectl", "create", "-f", olmURL+"/crds.yaml").Output() if olmCRDErr != nil { fmt.Println("") @@ -929,7 +929,7 @@ func installOLMCertManager() { certManagerInstalled, _ := exec.Command("kubectl", "get", "deployment", "cert-manager-webhook", "-n", "operators").Output() if len(certManagerInstalled) > 0 { - cfmt.Println("{{✓ Cert Manager allready installed}}::lightGreen") + cfmt.Println("{{✓ Cert Manager already installed}}::lightGreen") return }