From 67f3b8aa6deeb8bbcb5a751bcdf544f5dac95208 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 31 Dec 2019 01:01:32 +0000 Subject: [PATCH] add arm support and additional features (#1) Signed-off-by: Adam Crowder --- .version | 2 +- CHANGELOG.md | 23 ++++++ Makefile | 7 +- cmd/dc-installer/main.go | 30 +++++-- internal/configuration/input.go | 118 ++++++++++++++++++++++++++-- internal/configuration/os.go | 6 ++ internal/configuration/variables.go | 39 +++++---- internal/dragonchain/confirm.go | 9 ++- internal/dragonchain/installer.go | 6 +- internal/dragonchain/openfaas.go | 2 +- internal/dragonchain/prepare.go | 53 ++++++++++--- internal/dragonchain/registry.go | 18 ++++- internal/helm/configure.go | 51 ++++++++++-- internal/helm/installer.go | 7 +- internal/kubectl/installer.go | 4 +- internal/minikube/configure.go | 66 +++++++++++++--- internal/minikube/installer.go | 4 +- internal/virtualbox/installer.go | 3 + scripts/get_installer.bash | 2 +- 19 files changed, 378 insertions(+), 72 deletions(-) diff --git a/.version b/.version index a076e7f..e07d136 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -v0.5.1 \ No newline at end of file +v0.6.0 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4896a23..3c622d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## v0.6.0 + +- **Features:** + - Use new dragonchain version 4.3.2 + - Add support for vmdriver=none with minikube on linux + - Add partial-support for arm64 on linux (using vmdriver=none option) (experimental; not yet fully working due to [minikube support](https://github.com/kubernetes/minikube/issues/5667)) + - Use [local-path-provisioner](https://github.com/rancher/local-path-provisioner) for pvc storage + - Add support to restart installation with previous installation configuration +- **Packaging:** + - Use openfaas chart version 5.4.0 + - Use docker-registry chart version 1.9.1 + - Update default installed helm to v3.0.2 + - Update default installed kubectl to v1.17.0 + - Update default installed minikube to v1.6.2 + - Update default installed virtualbox to v6.1.0 + +## v0.5.1 + +- **Features:** + - Use new Dragonchain version 4.3.0 +- **Bugs:** + - Make initializing helm more reliable + ## v0.5.0 - **Features:** diff --git a/Makefile b/Makefile index 207b858..75dd6c3 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,13 @@ windows: GOOS=$(os) GOARCH=amd64 go build -v -ldflags '-s -w $(LDFLAGS)' -o release/$(BINARY)-$(os)-amd64.exe github.com/dragonchain/dragonchain-installer/cmd/dc-installer rm cmd/dc-installer/windows_amd64.syso +.PHONY: linux-arm64 +linux-arm64: + mkdir -p release + GOOS=linux GOARCH=arm64 go build -v -ldflags '-s -w $(LDFLAGS)' -o release/$(BINARY)-linux-arm64 github.com/dragonchain/dragonchain-installer/cmd/dc-installer + .PHONY: release -release: linux darwin windows +release: linux linux-arm64 darwin windows clean: rm -rf release diff --git a/cmd/dc-installer/main.go b/cmd/dc-installer/main.go index 27fb491..8593a11 100644 --- a/cmd/dc-installer/main.go +++ b/cmd/dc-installer/main.go @@ -36,15 +36,18 @@ func installer() { if err := minikube.InstallMinikubeIfNecessary(); err != nil { fatalLog(err) } - if err := virtualbox.InstallVirtualBoxIfNecessary(); err != nil { - fatalLog(err) - } - fmt.Print("\nAll dependencies installed\nConfiguring dependencies now\n\n") + fmt.Print("\nBase dependencies installed\nConfiguring dependencies now\n\n") config, err := configuration.PromptForUserConfiguration() if err != nil { fatalLog(err) } - if err := minikube.StartMinikubeCluster(); err != nil { + if config.UseVM { + fmt.Print("Virtualbox required for minikube VM. Checking and installing if necessary\n") + if err := virtualbox.InstallVirtualBoxIfNecessary(); err != nil { + fatalLog(err) + } + } + if err := minikube.StartMinikubeCluster(config.UseVM); err != nil { fatalLog(err) } if err := helm.InitializeHelm(); err != nil { @@ -53,8 +56,10 @@ func installer() { if err := dragonchain.SetupDragonchainPreReqs(config); err != nil { fatalLog(err) } - if err := virtualbox.ConfigureVirtualboxVM(config); err != nil { - fatalLog(err) + if config.UseVM { + if err := virtualbox.ConfigureVirtualboxVM(config); err != nil { + fatalLog(err) + } } fmt.Print("\nConfiguration of dependencies complete\nNow installing Dragonchain\n") if err := dragonchain.InstallDragonchain(config); err != nil { @@ -66,7 +71,7 @@ func installer() { fatalLog(err) } fmt.Print("Dragonchain public id is: " + pubID + "\n\n") - startCommand, stopCommand := minikube.FriendlyStartStopCommand() + startCommand, stopCommand := minikube.FriendlyStartStopCommand(config.UseVM) fmt.Print("In order to stop the dragonchain, run the following command in a terminal:\n" + stopCommand + "\n\n") fmt.Print("In order to restart the dragonchain, run the following command in a terminal:\n" + startCommand + "\n\n") if err := configuration.InstallDragonchainCredentials(config, pubID); err != nil { @@ -98,6 +103,15 @@ func installer() { } func main() { + if !(configuration.Windows || configuration.Linux || configuration.Macos) { + fatalLog("Unsupported OS") + } + if !configuration.AMD64 { + // Only non-amd64 architecture supported is arm64 on linux + if !(configuration.ARM64 && configuration.Linux) { + fatalLog("Unsupported OS/Architecture") + } + } if len(os.Args) > 1 && (os.Args[1] == "-V" || os.Args[1] == "--version" || os.Args[1] == "version") { fmt.Println(configuration.Version) } else { diff --git a/internal/configuration/input.go b/internal/configuration/input.go index 3702a4d..bb1b361 100644 --- a/internal/configuration/input.go +++ b/internal/configuration/input.go @@ -2,11 +2,14 @@ package configuration import ( "bufio" + "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "os" + "os/exec" + "path/filepath" "regexp" "strconv" "strings" @@ -16,12 +19,13 @@ import ( // Configuration is all of the data needed to configure a new chain type Configuration struct { - Level int - Name string - EndpointURL string - Port int - InternalID string - RegistrationToken string + Level int `json:"Level"` + Name string `json:"Name"` + EndpointURL string `json:"EndpointURL"` + Port int `json:"Port"` + InternalID string `json:"InternalID"` + RegistrationToken string `json:"RegistrationToken"` + UseVM bool `json:"UseVM"` PrivateKey string HmacID string HmacKey string @@ -29,6 +33,33 @@ type Configuration struct { var lowerCharNum = []byte("abcdefghijklmnopqrstuvxyz0123456789") +func configFilePath() (string, error) { + credentialFolder, err := credentialFolderPath() + if err != nil { + return "", err + } + return filepath.Join(credentialFolder, "installation_config"), nil +} + +func checkExistingConfig() (*Configuration, error) { + existingConfigFile, err := configFilePath() + if err != nil { + return nil, err + } + if _, err := os.Stat(existingConfigFile); os.IsNotExist(err) { + return nil, err + } + file, err := ioutil.ReadFile(existingConfigFile) + if err != nil { + return nil, err + } + existingConf := new(Configuration) + if err = json.Unmarshal([]byte(file), existingConf); err != nil { + return nil, err + } + return existingConf, nil +} + func getPublicIP() (string, error) { resp, err := http.Get("https://ifconfig.co/") if err != nil { @@ -161,13 +192,74 @@ func getEndpoint(port int) (string, error) { return endpoint, nil } +func getVMDriver() (bool, error) { + if !Linux { + // VM Driver must be used if not on linux + return true, nil + } + if !AMD64 { + // VM Driver false is required if not AMD64 + return false, nil + } + driver, err := getUserInput("Would you like to use your machine's native docker and run kubernetes outside of a VM? (yes/no) ") + if err != nil { + return false, err + } + driver = strings.ToLower(driver) + if driver == "y" || driver == "yes" { + // ensure docker is installed and running + cmd := exec.Command("sudo", "docker", "version") + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + if err := cmd.Run(); err != nil { + return false, errors.New("Error checking for running docker daemon:\n" + err.Error()) + } + return false, nil + } else if driver == "n" || driver == "no" { + return true, nil + } + return false, errors.New("Must answer yes/no") +} + // PromptForUserConfiguration get user input for all the necessary configurable variables of a Dragonchain func PromptForUserConfiguration() (*Configuration, error) { + // Check for existing configuration from previous run first + existingConf, err := checkExistingConfig() + if err == nil { + answer, err := getUserInput(`Existing config found: + Level: ` + strconv.Itoa(existingConf.Level) + ` + Name: ` + existingConf.Name + ` + EndpointURL: ` + existingConf.EndpointURL + ` + Port: ` + strconv.Itoa(existingConf.Port) + ` + ChainID: ` + existingConf.InternalID + ` + MatchmakingToken: ` + existingConf.RegistrationToken + ` + UseVM: ` + strconv.FormatBool(existingConf.UseVM) + ` + Would you like to use this config? (yes/no) `) + if err != nil { + return nil, err + } + answer = strings.ToLower(answer) + if answer == "y" || answer == "yes" { + return existingConf, nil + } else if answer == "n" || answer == "no" { + // Nothing happens, simply continue as normal + } else { + return nil, errors.New("Must answer yes/no") + } + } + // Get desired vm usage + vmDriver, err := getVMDriver() + if err != nil { + return nil, err + } // Get desired level level, err := getLevel() if err != nil { return nil, err } + if level == 1 && !AMD64 { + return nil, errors.New("Level 1 chains are not supported on your cpu architecture") + } // Get desired name name, err := getName() if err != nil { @@ -193,7 +285,7 @@ func PromptForUserConfiguration() (*Configuration, error) { if err != nil { return nil, err } - // Construct and return the config object + // Construct and save the config object config := new(Configuration) config.Level = level config.Name = name @@ -201,5 +293,17 @@ func PromptForUserConfiguration() (*Configuration, error) { config.Port = port config.InternalID = internalID config.RegistrationToken = registrationToken + config.UseVM = vmDriver + configJSON, err := json.Marshal(config) + if err != nil { + return nil, err + } + configFile, err := configFilePath() + if err != nil { + return nil, err + } + if err = ioutil.WriteFile(configFile, configJSON, 0664); err != nil { + return nil, err + } return config, nil } diff --git a/internal/configuration/os.go b/internal/configuration/os.go index 24b481c..6904574 100644 --- a/internal/configuration/os.go +++ b/internal/configuration/os.go @@ -12,3 +12,9 @@ var Macos bool = runtime.GOOS == "darwin" // Linux boolean if running on windows var Linux bool = runtime.GOOS == "linux" + +// AMD64 boolean if running on amd64 architecture +var AMD64 bool = runtime.GOARCH == "amd64" + +// ARM64 boolean if running on arm64 architecture +var ARM64 bool = runtime.GOARCH == "arm64" diff --git a/internal/configuration/variables.go b/internal/configuration/variables.go index 853fa9f..22da71a 100644 --- a/internal/configuration/variables.go +++ b/internal/configuration/variables.go @@ -4,13 +4,13 @@ package configuration var Version string // DragonchainHelmVersion helm version of dragonchain to use -var DragonchainHelmVersion = "1.0.4" +var DragonchainHelmVersion = "1.0.6" // OpenfaasHelmVersion helm version of openfaas (faas-netes) to use -var OpenfaasHelmVersion = "5.2.2" +var OpenfaasHelmVersion = "5.4.0" // RegistryHelmVersion helm version of docker container registry to use -var RegistryHelmVersion = "1.8.3" +var RegistryHelmVersion = "1.9.1" // RegistryIP the clusterip to use for the docker registry deployment var RegistryIP = "10.98.76.54" @@ -31,40 +31,49 @@ var MinikubeVMMemory = "4000mb" var MinikubeCpus = 2 // LinuxVirtualboxLink direct link for linux virtualbox installer download -var LinuxVirtualboxLink = "https://download.virtualbox.org/virtualbox/6.0.14/VirtualBox-6.0.14-133895-Linux_amd64.run" +var LinuxVirtualboxLink = "https://download.virtualbox.org/virtualbox/6.1.0/VirtualBox-6.1.0-135406-Linux_amd64.run" // MacosVirtualboxLink direct link for macos virtualbox installer download -var MacosVirtualboxLink = "https://download.virtualbox.org/virtualbox/6.0.14/VirtualBox-6.0.14-133895-OSX.dmg" +var MacosVirtualboxLink = "https://download.virtualbox.org/virtualbox/6.1.0/VirtualBox-6.1.0-135406-OSX.dmg" // WindowsVirtualboxLink direct link for windows virtualbox installer download -var WindowsVirtualboxLink = "https://download.virtualbox.org/virtualbox/6.0.14/VirtualBox-6.0.14-133895-Win.exe" +var WindowsVirtualboxLink = "https://download.virtualbox.org/virtualbox/6.1.0/VirtualBox-6.1.0-135406-Win.exe" + +// LinuxMinikubeArm64Link direct link for minikube aarch64 executable +var LinuxMinikubeArm64Link = "https://storage.googleapis.com/minikube/releases/v1.6.2/minikube-linux-arm64" // LinuxMinikubeLink direct link for linux minikube executable -var LinuxMinikubeLink = "https://github.com/kubernetes/minikube/releases/download/v1.5.2/minikube-linux-amd64" +var LinuxMinikubeLink = "https://storage.googleapis.com/minikube/releases/v1.6.2/minikube-linux-amd64" // MacosMinikubeLink direct link for macos minikube executable -var MacosMinikubeLink = "https://github.com/kubernetes/minikube/releases/download/v1.5.2/minikube-darwin-amd64" +var MacosMinikubeLink = "https://storage.googleapis.com/minikube/releases/v1.6.2/minikube-darwin-amd64" // WindowsMinikubeLink direct link for windows minikube executable -var WindowsMinikubeLink = "https://github.com/kubernetes/minikube/releases/download/v1.5.2/minikube-windows-amd64.exe" +var WindowsMinikubeLink = "https://storage.googleapis.com/minikube/releases/v1.6.2/minikube-windows-amd64.exe" // LinuxHelmLink direct link for linux helm package -var LinuxHelmLink = "https://get.helm.sh/helm-v2.16.1-linux-amd64.tar.gz" +var LinuxHelmLink = "https://get.helm.sh/helm-v3.0.2-linux-amd64.tar.gz" + +// LinuxHelmArm64Link direct link for linux arm64 helm package +var LinuxHelmArm64Link = "https://get.helm.sh/helm-v3.0.2-linux-arm64.tar.gz" // MacosHelmLink direct link for macos helm package -var MacosHelmLink = "https://get.helm.sh/helm-v2.16.1-darwin-amd64.tar.gz" +var MacosHelmLink = "https://get.helm.sh/helm-v3.0.2-darwin-amd64.tar.gz" // WindowsHelmLink direct link for windows helm package -var WindowsHelmLink = "https://get.helm.sh/helm-v2.16.1-windows-amd64.zip" +var WindowsHelmLink = "https://get.helm.sh/helm-v3.0.2-windows-amd64.zip" // LinuxKubectlLink direct link for linux kubectl executable -var LinuxKubectlLink = "https://storage.googleapis.com/kubernetes-release/release/v1.16.2/bin/linux/amd64/kubectl" +var LinuxKubectlLink = "https://storage.googleapis.com/kubernetes-release/release/v1.17.0/bin/linux/amd64/kubectl" + +// LinuxKubectlArm64Link directl link for linux arm64 kubectl executable +var LinuxKubectlArm64Link = "https://storage.googleapis.com/kubernetes-release/release/v1.17.0/bin/linux/arm64/kubectl" // MacosKubectlLink direct link for macos kubectl executable -var MacosKubectlLink = "https://storage.googleapis.com/kubernetes-release/release/v1.16.2/bin/darwin/amd64/kubectl" +var MacosKubectlLink = "https://storage.googleapis.com/kubernetes-release/release/v1.17.0/bin/darwin/amd64/kubectl" // WindowsKubectlLink direct link for windows kubectl executable -var WindowsKubectlLink = "https://storage.googleapis.com/kubernetes-release/release/v1.16.2/bin/windows/amd64/kubectl.exe" +var WindowsKubectlLink = "https://storage.googleapis.com/kubernetes-release/release/v1.17.0/bin/windows/amd64/kubectl.exe" // SetDefaultCredentials indicates whether or not to set the default chain whe configuring the credentials ini file var SetDefaultCredentials = true diff --git a/internal/dragonchain/confirm.go b/internal/dragonchain/confirm.go index b464eb7..c4293a3 100644 --- a/internal/dragonchain/confirm.go +++ b/internal/dragonchain/confirm.go @@ -3,6 +3,7 @@ package dragonchain import ( "encoding/json" "errors" + "fmt" "os" "os/exec" "strings" @@ -65,10 +66,14 @@ func dragonchainPubIDRecurse(config *configuration.Configuration, tries int) (st } func waitForDragonchainToBeReady(config *configuration.Configuration) error { - // Wait up to 90-ish seconds for dragonchain to be ready before erroring - for i := 0; i < 90; i++ { + // Wait up to 120-ish seconds for dragonchain to be ready before erroring + for i := 0; i < 120; i++ { // Wait before checking time.Sleep(1 * time.Second) + // Print a '.' every 10 seconds to show that the program is still running + if i%10 == 0 { + fmt.Print(".") + } cmd := exec.Command("kubectl", "get", "pod", "-n", "dragonchain", "-l", "dragonchainId="+config.InternalID, "-o", "json", "--context="+configuration.MinikubeContext) cmd.Stderr = os.Stderr output, err := cmd.Output() diff --git a/internal/dragonchain/installer.go b/internal/dragonchain/installer.go index 919f5b1..1a7048f 100644 --- a/internal/dragonchain/installer.go +++ b/internal/dragonchain/installer.go @@ -102,7 +102,7 @@ func getExistingSecret(config *configuration.Configuration) error { func upsertDragonchainHelmDeployment(config *configuration.Configuration) error { setStringStr := "global.environment.LEVEL=" + strconv.Itoa(config.Level) - setStr := "global.environment.DRAGONCHAIN_NAME=" + config.Name + ",global.environment.REGISTRATION_TOKEN=" + config.RegistrationToken + ",global.environment.INTERNAL_ID=" + config.InternalID + ",global.environment.DRAGONCHAIN_ENDPOINT=" + config.EndpointURL + ",service.port=" + strconv.Itoa(config.Port) + setStr := "dragonchain.storage.spec.storageClassName=local-path,redis.storage.spec.storageClassName=local-path,redisearch.storage.spec.storageClassName=local-path,global.environment.DRAGONCHAIN_NAME=" + config.Name + ",global.environment.REGISTRATION_TOKEN=" + config.RegistrationToken + ",global.environment.INTERNAL_ID=" + config.InternalID + ",global.environment.DRAGONCHAIN_ENDPOINT=" + config.EndpointURL + ",service.port=" + strconv.Itoa(config.Port) if config.Level == 1 { setStr += ",faas.gateway=http://gateway.openfaas:8080,faas.mountFaasSecret=true,faas.registry=" + configuration.RegistryIP + ":" + strconv.Itoa(configuration.RegistryPort) } @@ -134,7 +134,9 @@ func InstallDragonchain(config *configuration.Configuration) error { } fmt.Println("Dragonchain helm deployment complete. Waiting for chain to be ready.") // Wait for the deployment to be ready before continuing - if err := waitForDragonchainToBeReady(config); err != nil { + err := waitForDragonchainToBeReady(config) + fmt.Print("\n") + if err != nil { return err } return nil diff --git a/internal/dragonchain/openfaas.go b/internal/dragonchain/openfaas.go index eda697b..e71f85b 100644 --- a/internal/dragonchain/openfaas.go +++ b/internal/dragonchain/openfaas.go @@ -85,7 +85,7 @@ func createOpenFaasDeployment() error { return errors.New("Error creating openfaas kubernetes secret:\n" + err.Error()) } // Install openfaas - cmd = exec.Command("helm", "upgrade", "--install", "openfaas", "openfaas/openfaas", "--namespace", "openfaas", "--set", "basic_auth=true,functionNamespace=openfaas-fn,async=false,exposeServices=false,alertmanager.create=false,prometheus.create=false", "--version", configuration.OpenfaasHelmVersion, "--kube-context", configuration.MinikubeContext) + cmd = exec.Command("helm", "upgrade", "--install", "openfaas", "openfaas/openfaas", "--namespace", "openfaas", "--set", "basic_auth=true,generateBasicAuth=false,functionNamespace=openfaas-fn,async=false,exposeServices=false,alertmanager.create=false,prometheus.create=false", "--version", configuration.OpenfaasHelmVersion, "--kube-context", configuration.MinikubeContext) cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return errors.New("Error helm deploying openfaas:\n" + err.Error()) diff --git a/internal/dragonchain/prepare.go b/internal/dragonchain/prepare.go index 9e7323b..00fb584 100644 --- a/internal/dragonchain/prepare.go +++ b/internal/dragonchain/prepare.go @@ -5,23 +5,36 @@ import ( "fmt" "os" "os/exec" + "strconv" + "time" "github.com/dragonchain/dragonchain-installer/internal/configuration" + "github.com/dragonchain/dragonchain-installer/internal/helm" ) -func doesHelmDeploymentExist(name string) (bool, error) { - cmd := exec.Command("helm", "list", name, "--kube-context", configuration.MinikubeContext) - cmd.Stderr = os.Stderr - output, err := cmd.Output() +func doesHelmDeploymentExist(name string, namespace string) (bool, error) { + helmVersion, err := helm.GetHelmMajorVersion() if err != nil { - return false, errors.New("Error checking helm for " + name + ":\n" + err.Error()) + return false, err + } + cmd := exec.Command("helm", "get", "notes", name, "--kube-context", configuration.MinikubeContext) + if helmVersion > 2 { + cmd = exec.Command("helm", "get", "notes", name, "-n", namespace, "--kube-context", configuration.MinikubeContext) + } + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + // Will get an error if the deployment does not exist + return false, nil } - // If there is output, a helm installation already exists for this deployment - return len(output) > 0, nil + // If command ran successfully, a helm installation already exists for this deployment + return true, nil } // SetupDragonchainPreReqs sets up kubernetes resource requirements for dragonchain func SetupDragonchainPreReqs(config *configuration.Configuration) error { + if err := exec.Command("kubectl", "apply", "-f", "https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml").Run(); err != nil { + return errors.New("Error creating local path provisioner:\n" + err.Error()) + } if exec.Command("kubectl", "get", "namespace", "dragonchain", "--context="+configuration.MinikubeContext).Run() != nil { // Create the dragonchain namespace if necessary fmt.Println("Creating dragonchain namespace") @@ -34,7 +47,7 @@ func SetupDragonchainPreReqs(config *configuration.Configuration) error { // Set up l1 dependencies if needed if config.Level == 1 { // Set up openfaas - exists, err := doesHelmDeploymentExist("openfaas") + exists, err := doesHelmDeploymentExist("openfaas", "openfaas") if err != nil { return errors.New("Error checking for existing openfaas installation:\n" + err.Error()) } @@ -44,8 +57,30 @@ func SetupDragonchainPreReqs(config *configuration.Configuration) error { return err } } + if !config.UseVM { + // Try to backup old docker daemon config if it exists + cmd := exec.Command("sudo", "mv", "/etc/docker/daemon.json", "/etc/docker/daemon.json.bak") + cmd.Stdin = os.Stdin + cmd.Run() + // If using native machine docker, need to ensure that insecure registry for the registry is set on the daemon + dockerDaemonJSON := "{\\\"insecure-registries\\\":[\\\"" + configuration.RegistryIP + ":" + strconv.Itoa(configuration.RegistryPort) + "\\\"]}" + cmd = exec.Command("sh", "-c", "echo "+dockerDaemonJSON+" | sudo tee /etc/docker/daemon.json") + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + if err := cmd.Run(); err != nil { + return errors.New("Error setting insecure registry setting with docker daemon:\n" + err.Error()) + } + cmd = exec.Command("sudo", "service", "docker", "restart") + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + if err := cmd.Run(); err != nil { + return errors.New("Error restarting docker daemon:\n" + err.Error()) + } + // Briefly wait for containers to come back up after restarting + time.Sleep(10 * time.Second) + } // Set up docker registry - exists, err = doesHelmDeploymentExist("registry") + exists, err = doesHelmDeploymentExist("registry", "registry") if err != nil { return errors.New("Error checking for existing container registry installation:\n" + err.Error()) } diff --git a/internal/dragonchain/registry.go b/internal/dragonchain/registry.go index 912a4dc..35f21e1 100644 --- a/internal/dragonchain/registry.go +++ b/internal/dragonchain/registry.go @@ -1,6 +1,7 @@ package dragonchain import ( + "bytes" "errors" "os" "os/exec" @@ -9,12 +10,25 @@ import ( "github.com/dragonchain/dragonchain-installer/internal/configuration" ) +var registryNamespacesYaml = []byte(` +apiVersion: v1 +kind: Namespace +metadata: + name: registry`) + func createDockerRegistryDeployment() error { + // Create the necessary namespaces + cmd := exec.Command("kubectl", "apply", "--context="+configuration.MinikubeContext, "-f", "-") + cmd.Stderr = os.Stderr + cmd.Stdin = bytes.NewBuffer(registryNamespacesYaml) + if err := cmd.Run(); err != nil { + return errors.New("Error creating registry namespace:\n" + err.Error()) + } // Install the registry - cmd := exec.Command("helm", "upgrade", "--install", "registry", "stable/docker-registry", "--namespace", "registry", "--set", "persistence.enabled=true,persistence.storageClass=standard,persistence.deleteEnabled=true,service.type=ClusterIP,service.clusterIP="+configuration.RegistryIP+",service.port="+strconv.Itoa(configuration.RegistryPort), "--version", configuration.RegistryHelmVersion, "--kube-context", configuration.MinikubeContext) + cmd = exec.Command("helm", "upgrade", "--install", "registry", "stable/docker-registry", "--namespace", "registry", "--set", "persistence.enabled=true,persistence.storageClass=local-path,persistence.deleteEnabled=true,service.type=ClusterIP,service.clusterIP="+configuration.RegistryIP+",service.port="+strconv.Itoa(configuration.RegistryPort), "--version", configuration.RegistryHelmVersion, "--kube-context", configuration.MinikubeContext) cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { - return errors.New("Error helm deploying openfaas:\n" + err.Error()) + return errors.New("Error helm deploying registry:\n" + err.Error()) } return nil } diff --git a/internal/helm/configure.go b/internal/helm/configure.go index b56526b..2ec1eeb 100644 --- a/internal/helm/configure.go +++ b/internal/helm/configure.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "os/exec" + "strings" "time" "github.com/dragonchain/dragonchain-installer/internal/configuration" @@ -47,15 +48,39 @@ func waitForTillerToBeReady() error { return errors.New("Tiller pod failed to become ready") } +// GetHelmMajorVersion gets the major version of helm (either 2 or 3) +func GetHelmMajorVersion() (int, error) { + cmd := exec.Command("helm", "version", "-c", "--short") + cmd.Stderr = os.Stderr + out, err := cmd.Output() + if err != nil { + return 0, errors.New("Unable to get helm version:\n" + err.Error()) + } + versionOutput := string(out) + if strings.Contains(versionOutput, "v2.") { + return 2, nil + } else if strings.Contains(versionOutput, "v3.") { + return 3, nil + } + return 0, errors.New("Unable to parse helm version string") +} + // InitializeHelm intializes helm both locally, and in the minikube cluster func InitializeHelm() error { fmt.Println("Configuring helm") - cmd := exec.Command("helm", "init", "--upgrade", "--kube-context", configuration.MinikubeContext) - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return errors.New("Initializing helm failed:\n" + err.Error()) + helmVersion, err := GetHelmMajorVersion() + if err != nil { + return err } - cmd = exec.Command("helm", "repo", "add", "dragonchain", "https://dragonchain-charts.s3.amazonaws.com") + // Only helm v2 requires tiller initialization + if helmVersion == 2 { + cmd := exec.Command("helm", "init", "--upgrade", "--kube-context", configuration.MinikubeContext) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return errors.New("Initializing helm failed:\n" + err.Error()) + } + } + cmd := exec.Command("helm", "repo", "add", "dragonchain", "https://dragonchain-charts.s3.amazonaws.com") cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return errors.New("Adding dragonchain helm repo failed:\n" + err.Error()) @@ -65,14 +90,24 @@ func InitializeHelm() error { if err := cmd.Run(); err != nil { return errors.New("Adding openfaas helm repo failed:\n" + err.Error()) } + if helmVersion >= 3 { + // Stable repository is not added by default in helm 3+ + cmd = exec.Command("helm", "repo", "add", "stable", "https://kubernetes-charts.storage.googleapis.com") + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return errors.New("Adding stable helm repo failed:\n" + err.Error()) + } + } cmd = exec.Command("helm", "repo", "update") cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return errors.New("Updating helm repo failed (are you connected to the internet?):\n" + err.Error()) } - time.Sleep(3 * time.Second) - if err := waitForTillerToBeReady(); err != nil { - return err + if helmVersion == 2 { + time.Sleep(3 * time.Second) + if err := waitForTillerToBeReady(); err != nil { + return err + } } return nil } diff --git a/internal/helm/installer.go b/internal/helm/installer.go index b02e7ab..e7546b8 100644 --- a/internal/helm/installer.go +++ b/internal/helm/installer.go @@ -52,11 +52,14 @@ func InstallHelmIfNecessary() error { tempZip := filepath.Join(tempDir, "helm.tar.gz") downloadLink := configuration.LinuxHelmLink extractedFolder := "linux-amd64" - unixInstallPath := filepath.Join("/", "usr", "local", "bin", "helm") - if configuration.Macos { + if configuration.ARM64 { + downloadLink = configuration.LinuxHelmArm64Link + extractedFolder = "linux-arm64" + } else if configuration.Macos { downloadLink = configuration.MacosHelmLink extractedFolder = "darwin-amd64" } + unixInstallPath := filepath.Join("/", "usr", "local", "bin", "helm") // Download the helm gzip package if err := downloader.DownloadFile(tempZip, downloadLink); err != nil { return err diff --git a/internal/kubectl/installer.go b/internal/kubectl/installer.go index 44cb170..fff6b0c 100644 --- a/internal/kubectl/installer.go +++ b/internal/kubectl/installer.go @@ -42,7 +42,9 @@ func InstallKubectlIfNecessary() error { var allowExecute os.FileMode = 0775 unixBinaryPath := filepath.Join("/", "usr", "local", "bin", "kubectl") downloadLink := configuration.LinuxKubectlLink - if configuration.Macos { + if configuration.ARM64 { + downloadLink = configuration.LinuxKubectlArm64Link + } else if configuration.Macos { downloadLink = configuration.MacosKubectlLink } if err := downloader.DownloadFile(tempPath, downloadLink); err != nil { diff --git a/internal/minikube/configure.go b/internal/minikube/configure.go index 878edb9..78c70eb 100644 --- a/internal/minikube/configure.go +++ b/internal/minikube/configure.go @@ -8,6 +8,7 @@ import ( "os/exec" "path/filepath" "strconv" + "strings" "github.com/dragonchain/dragonchain-installer/internal/configuration" ) @@ -18,7 +19,11 @@ type minikubeProfileList struct { }) `json:"valid"` } -func existingMinikubeClusterExists() (bool, error) { +func existingMinikubeClusterExists(useVM bool) (bool, error) { + if !useVM { + // When using vmdriver none, we cannot use minikube profiles, and start/resume command is the same + return true, nil + } // Make sure minikube profiles folder exists or minikube can unexpectedly fail: https://github.com/kubernetes/minikube/issues/5898 homeDir, err := os.UserHomeDir() if err != nil { @@ -47,14 +52,19 @@ func existingMinikubeClusterExists() (bool, error) { } // FriendlyStartStopCommand returns the strings of the start/stop commands that a user can use to start stop minikube (and thus the dragonchain) -func FriendlyStartStopCommand() (startCommand string, stopCommand string) { - startCommand = "minikube start -p " + configuration.MinikubeContext + " --kubernetes-version=" + configuration.KubernetesVersion - stopCommand = "minikube stop -p " + configuration.MinikubeContext +func FriendlyStartStopCommand(useVM bool) (startCommand string, stopCommand string) { + if useVM { + startCommand = "minikube start -p " + configuration.MinikubeContext + " --kubernetes-version=" + configuration.KubernetesVersion + stopCommand = "minikube stop -p " + configuration.MinikubeContext + } else { + startCommand = "sudo minikube start --kubernetes-version=" + configuration.KubernetesVersion + stopCommand = "sudo minikube stop" + } return } // StartMinikubeCluster starts (or creates and starts) the minikube cluster with a configured profile -func StartMinikubeCluster() error { +func StartMinikubeCluster(useVM bool) error { // Switch current directory to the systemroot on C:\ if running on windows to avoid minikube bug: https://github.com/kubernetes/minikube/issues/1574 if configuration.Windows { systemRoot, exists := os.LookupEnv("SYSTEMROOT") @@ -65,23 +75,57 @@ func StartMinikubeCluster() error { return errors.New("Error switching directory:\n" + err.Error()) } } - exists, err := existingMinikubeClusterExists() + exists, err := existingMinikubeClusterExists(useVM) if err != nil { return err } os.Setenv("MINIKUBE_IN_STYLE", "false") var minikubeStartCmd *exec.Cmd - if exists { - fmt.Println("\nStarting existing minikube cluster '" + configuration.MinikubeContext + "'; This can take a while") - minikubeStartCmd = exec.Command("minikube", "start", "-p", configuration.MinikubeContext, "--kubernetes-version="+configuration.KubernetesVersion) + if !useVM { + fmt.Println("\nStarting minikube cluster; This can take a while") + minikubeStartCmd = exec.Command("sudo", "minikube", "start", "--kubernetes-version="+configuration.KubernetesVersion, "--vm-driver=none") + configuration.MinikubeContext = "minikube" } else { - fmt.Println("\nStarting new minikube cluster '" + configuration.MinikubeContext + "'; This can take a while") - minikubeStartCmd = exec.Command("minikube", "start", "-p", configuration.MinikubeContext, "--kubernetes-version="+configuration.KubernetesVersion, "--vm-driver=virtualbox", "--memory="+configuration.MinikubeVMMemory, "--cpus="+strconv.Itoa(configuration.MinikubeCpus)) + if exists { + fmt.Println("\nStarting existing minikube cluster '" + configuration.MinikubeContext + "'; This can take a while") + minikubeStartCmd = exec.Command("minikube", "start", "-p", configuration.MinikubeContext, "--kubernetes-version="+configuration.KubernetesVersion) + } else { + fmt.Println("\nStarting new minikube cluster '" + configuration.MinikubeContext + "'; This can take a while") + minikubeStartCmd = exec.Command("minikube", "start", "-p", configuration.MinikubeContext, "--kubernetes-version="+configuration.KubernetesVersion, "--vm-driver=virtualbox", "--memory="+configuration.MinikubeVMMemory, "--cpus="+strconv.Itoa(configuration.MinikubeCpus)) + } } minikubeStartCmd.Stdout = os.Stdout minikubeStartCmd.Stderr = os.Stderr + minikubeStartCmd.Stdin = os.Stdin if err := minikubeStartCmd.Run(); err != nil { return errors.New("Failed to start minikube. Resolve errors to continue:\n" + err.Error()) } + if !useVM { + // Minikube with no vm driver writes kube configs as root; we need to fix that + cmd := exec.Command("id", "-u") + cmd.Stderr = os.Stderr + userIDBytes, err := cmd.Output() + if err != nil { + return errors.New("Couldn't get current user id:\n" + err.Error()) + } + cmd = exec.Command("id", "-g") + cmd.Stderr = os.Stderr + groupIDBytes, err := cmd.Output() + if err != nil { + return errors.New("Couldn't get current user group:\n" + err.Error()) + } + userIDString := strings.TrimRight(string(userIDBytes), "\n") + groupIDString := strings.TrimRight(string(groupIDBytes), "\n") + home, exists := os.LookupEnv("HOME") + if !exists { + return errors.New("Couldn't find home directory (no HOME env var)") + } + cmd = exec.Command("sudo", "chown", "-R", userIDString+":"+groupIDString, filepath.Join(home, ".kube"), filepath.Join(home, ".minikube")) + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + if err := cmd.Run(); err != nil { + return errors.New("Was not able to chown config directories:\n" + err.Error()) + } + } return nil } diff --git a/internal/minikube/installer.go b/internal/minikube/installer.go index d7b8089..9a8c562 100644 --- a/internal/minikube/installer.go +++ b/internal/minikube/installer.go @@ -42,7 +42,9 @@ func InstallMinikubeIfNecessary() error { var allowExecute os.FileMode = 0775 unixBinaryPath := filepath.Join("/", "usr", "local", "bin", "minikube") downloadLink := configuration.LinuxMinikubeLink - if configuration.Macos { + if configuration.ARM64 { + downloadLink = configuration.LinuxMinikubeArm64Link + } else if configuration.Macos { downloadLink = configuration.MacosMinikubeLink } if err := downloader.DownloadFile(tempPath, downloadLink); err != nil { diff --git a/internal/virtualbox/installer.go b/internal/virtualbox/installer.go index dafc8e7..b61c005 100644 --- a/internal/virtualbox/installer.go +++ b/internal/virtualbox/installer.go @@ -20,6 +20,9 @@ func virtualBoxIsInstalled() bool { // InstallVirtualBoxIfNecessary checks if virtualbox is already installed, and installs it if necessary func InstallVirtualBoxIfNecessary() error { + if !configuration.AMD64 { + return errors.New("Cannot install virtualbox on non-amd64 architecture") + } if virtualBoxIsInstalled() { fmt.Println("virtualbox appears to already be installed") return nil diff --git a/scripts/get_installer.bash b/scripts/get_installer.bash index d73f8d9..ac913b1 100755 --- a/scripts/get_installer.bash +++ b/scripts/get_installer.bash @@ -2,7 +2,7 @@ set -e # Default version (change with new tagged releases) -VERSION="v0.5.0" +VERSION="v0.6.0" if [ -z "$HOME" ]; then echo "The \$HOME environment variable must be present"