From 55dfac7e82acd87e88c6c02066c54a31b9ac5ff8 Mon Sep 17 00:00:00 2001 From: Johan Siebens Date: Sun, 11 Oct 2020 07:48:39 +0200 Subject: [PATCH] Add Consul Service Mesh as an installable app Signed-off-by: Johan Siebens --- README.md | 1 + cmd/apps/consul_app.go | 172 +++++++++++++++++++++++++++++++++++++++++ cmd/install.go | 4 + 3 files changed, 177 insertions(+) create mode 100644 cmd/apps/consul_app.go diff --git a/README.md b/README.md index 6b2b698f8..8c4543f29 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,7 @@ Available Commands: argocd Install argocd cert-manager Install cert-manager chart Install the specified helm chart + consul-connect Install Consul Service Mesh cron-connector Install cron-connector for OpenFaaS crossplane Install Crossplane docker-registry Install a Docker registry diff --git a/cmd/apps/consul_app.go b/cmd/apps/consul_app.go new file mode 100644 index 000000000..7fc9b69ae --- /dev/null +++ b/cmd/apps/consul_app.go @@ -0,0 +1,172 @@ +// Copyright (c) arkade author(s) 2020. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +package apps + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "github.com/alexellis/arkade/pkg/apps" + "github.com/alexellis/arkade/pkg/types" + "log" + "os" + "path" + "strconv" + "strings" + + "github.com/alexellis/arkade/pkg/k8s" + + "github.com/alexellis/arkade/pkg" + "github.com/alexellis/arkade/pkg/config" + "github.com/alexellis/arkade/pkg/env" + "github.com/alexellis/arkade/pkg/helm" + "github.com/spf13/cobra" +) + +func MakeInstallConsul() *cobra.Command { + var consul = &cobra.Command{ + Use: "consul-connect", + Short: "Install Consul Service Mesh", + Long: `Install Consul Service Mesh to any Kubernetes cluster`, + Example: ` arkade install consul-connect`, + SilenceUsage: true, + } + + consul.Flags().StringP("namespace", "n", "consul-system", "The namespace used for installation") + consul.Flags().Bool("update-repo", true, "Update the helm repo") + consul.Flags().StringP("datacenter", "d", "dc1", "The name of the datacenter that the agents should register as") + consul.Flags().Bool("enable-connect-injector", true, "If true, all the resources necessary for the Connect injector process to run will be installed") + consul.Flags().Bool("enable-tls-encryption", true, "If true, TLS encryption across the cluster to verify authenticity of the Consul servers and clients is enabled") + consul.Flags().Bool("enable-gossip-encryption", true, "If true, Consul's gossip encryption is enabled") + consul.Flags().String("gossip-encryption-key", "", "The gossip encryption key; when empty, a new, random key is generated") + consul.Flags().Bool("manage-system-acls", true, "If true, the ACL tokens and policies for all Consul and consul-k8s components will automatically be managed") + consul.Flags().StringArray("set", []string{}, "Use custom flags or override existing flags \n(example --set=image=org/repo:tag)") + + consul.RunE = func(command *cobra.Command, args []string) error { + updateRepo, _ := consul.Flags().GetBool("update-repo") + + arch := k8s.GetNodeArchitecture() + fmt.Printf("Node architecture: %q\n", arch) + + if arch != IntelArch { + return fmt.Errorf(OnlyIntelArch) + } + + userPath, err := config.InitUserDir() + if err != nil { + return err + } + + clientArch, clientOS := env.GetClientArch() + + fmt.Printf("Client: %s, %s\n", clientArch, clientOS) + log.Printf("User dir established as: %s\n", userPath) + + namespace, _ := consul.Flags().GetString("namespace") + + overrides := map[string]string{} + overrides["global.name"] = "consul" + + datacenter, _ := command.Flags().GetString("datacenter") + overrides["global.datacenter"] = datacenter + + connectInjectorEnabled, _ := command.Flags().GetBool("enable-connect-injector") + overrides["connectInject.enabled"] = strings.ToLower(strconv.FormatBool(connectInjectorEnabled)) + + tlsEnabled, _ := command.Flags().GetBool("enable-tls-encryption") + overrides["global.tls.enabled"] = strings.ToLower(strconv.FormatBool(tlsEnabled)) + + manageSystemACLs, _ := command.Flags().GetBool("manage-system-acls") + overrides["global.acls.manageSystemACLs"] = strings.ToLower(strconv.FormatBool(manageSystemACLs)) + + gossipEncryptionEnabled, _ := command.Flags().GetBool("enable-gossip-encryption") + gossipEncryptionKey, _ := command.Flags().GetString("gossip-encryption-key") + + if gossipEncryptionEnabled && gossipEncryptionKey == "" { + gossipEncryptionKey, err = generateGossipEncryptionKey() + if err != nil { + return err + } + } + + _, nsErr := k8s.KubectlTask("create", "namespace", namespace) + if nsErr != nil && !strings.Contains(nsErr.Error(), "AlreadyExists") { + return nsErr + } + + if gossipEncryptionEnabled { + res, err := k8s.KubectlTask("create", "secret", "generic", + "consul-gossip-encryption-key", + "--namespace="+namespace, + "--from-literal", "key="+gossipEncryptionKey) + if err != nil { + return err + } else if len(res.Stderr) > 0 && strings.Contains(res.Stderr, "AlreadyExists") { + fmt.Println("[Warning] secret consul-gossip-encryption-key already exists and will be used.") + } else if len(res.Stderr) > 0 { + return fmt.Errorf("error from kubectl\n%q", res.Stderr) + } + + overrides["global.gossipEncryption.secretName"] = "consul-gossip-encryption-key" + overrides["global.gossipEncryption.secretKey"] = "key" + } + + customFlags, _ := command.Flags().GetStringArray("set") + + if err := mergeFlags(overrides, customFlags); err != nil { + return err + } + + consulOptions := types.DefaultInstallOptions(). + WithNamespace(namespace). + WithHelmPath(path.Join(userPath, ".helm")). + WithHelmRepo("hashicorp/consul"). + WithHelmURL("https://helm.releases.hashicorp.com"). + WithOverrides(overrides). + WithHelmUpdateRepo(updateRepo) + + if command.Flags().Changed("kubeconfig") { + kubeconfigPath, _ := command.Flags().GetString("kubeconfig") + consulOptions.WithKubeconfigPath(kubeconfigPath) + } + + os.Setenv("HELM_HOME", path.Join(userPath, ".helm")) + + _, err = helm.TryDownloadHelm(userPath, clientArch, clientOS) + if err != nil { + return err + } + + _, err = apps.MakeInstallChart(consulOptions) + if err != nil { + return err + } + + fmt.Println(consulInstallMsg) + return nil + } + + return consul +} + +func generateGossipEncryptionKey() (string, error) { + key := make([]byte, 32) + n, err := rand.Reader.Read(key) + if err != nil { + return "", err + } + if n != 32 { + return "", fmt.Errorf("couldn't read enough entropy, generate more entropy") + } + + return base64.StdEncoding.EncodeToString(key), nil +} + +const ConsulInfoMsg = `# Find out more at: +# https://www.consul.io/docs/k8s` + +const consulInstallMsg = `======================================================================= += Consul has been installed. = +=======================================================================` + + "\n\n" + ConsulInfoMsg + "\n\n" + pkg.ThanksForUsing diff --git a/cmd/install.go b/cmd/install.go index 266eb4430..7a36bfb6f 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -99,7 +99,11 @@ func GetApps() map[string]ArkadeApp { arkadeApps["registry-creds"] = NewArkadeApp(apps.MakeInstallRegistryCredsOperator, apps.RegistryCredsOperatorInfoMsg) arkadeApps["gitea"] = NewArkadeApp(apps.MakeInstallGitea, apps.GiteaInfoMsg) arkadeApps["kong-ingress"] = NewArkadeApp(apps.MakeInstallKongIngress, apps.KongIngressInfoMsg) +<<<<<<< HEAD arkadeApps["sealed-secret"] = NewArkadeApp(apps.MakeInstallSealedSecrets, apps.SealedSecretsInfoMsg) +======= + arkadeApps["consul-connect"] = NewArkadeApp(apps.MakeInstallConsul, apps.ConsulInfoMsg) +>>>>>>> Add Consul Service Mesh as an installable app // Special "chart" app - let a user deploy any helm chart arkadeApps["chart"] = NewArkadeApp(apps.MakeInstallChart, "") return arkadeApps