diff --git a/lib/install.go b/lib/install.go index e9a5b2f6..0aced6ee 100644 --- a/lib/install.go +++ b/lib/install.go @@ -10,7 +10,7 @@ import ( ) const ( - hashiURL = "https://releases.hashicorp.com/terraform/" + //hashiURL = "https://releases.hashicorp.com/terraform/" installFile = "terraform" installVersion = "terraform_" installPath = ".terraform.versions" @@ -50,29 +50,6 @@ func initialize() { } -// get install path variable value (windows os runtime support) -// func getInstallPath() string { -// return string(os.PathSeparator) + installPath + string(os.PathSeparator) -// } - -// get versioned install filename (windows os runtime support) -// func getVersionedInstallFileName(tfversion string) string { -// if runtime.GOOS == "windows" { -// return filepath.Join(getInstallLocation(), installVersion+tfversion+".exe") -// } - -// return getInstallLocation() + installVersion + tfversion -// } - -// get install filename (windows os runtime support) -// func getInstallFileName() string { -// if runtime.GOOS == "windows" { -// return filepath.Join(getInstallLocation(), installFile+".exe") -// } - -// return installLocation + installFile -// } - // getInstallLocation : get location where the terraform binary will be installed, // will create a directory in the home location if it does not exist func getInstallLocation() string { @@ -103,7 +80,7 @@ func getInstallLocation() string { } //Install : Install the provided version in the argument -func Install(tfversion string, binPath string) { +func Install(tfversion string, binPath string, mirror string) { if !ValidVersionFormat(tfversion) { fmt.Printf("The provided terraform version format does not exist - %s. Try `tfswitch -l` to see all available versions.\n", tfversion) @@ -153,7 +130,8 @@ func Install(tfversion string, binPath string) { /* if selected version already exist, */ /* proceed to download it from the hashicorp release page */ - url := hashiURL + tfversion + "/" + installVersion + tfversion + "_" + goos + "_" + goarch + ".zip" + artifactMirror := mirror + url := artifactMirror + tfversion + "/" + installVersion + tfversion + "_" + goos + "_" + goarch + ".zip" zipFile, errDownload := DownloadFromURL(installLocation, url) /* If unable to download file from url, exit(1) immediately */ diff --git a/lib/list_versions.go b/lib/list_versions.go index 2d598d23..9708091d 100644 --- a/lib/list_versions.go +++ b/lib/list_versions.go @@ -15,9 +15,9 @@ type tfVersionList struct { } //GetTFList : Get the list of available terraform version given the hashicorp url -func GetTFList(hashiURL string, preRelease bool) ([]string, error) { +func GetTFList(mirrorURL string, preRelease bool) ([]string, error) { - result, error := GetTFURLBody(hashiURL) + result, error := GetTFURLBody(mirrorURL) if error != nil { return nil, error } @@ -45,9 +45,9 @@ func GetTFList(hashiURL string, preRelease bool) ([]string, error) { } //GetTFLatest : Get the latest terraform version given the hashicorp url -func GetTFLatest(hashiURL string) (string, error) { +func GetTFLatest(mirrorURL string) (string, error) { - result, error := GetTFURLBody(hashiURL) + result, error := GetTFURLBody(mirrorURL) if error != nil { return "", error } @@ -66,9 +66,9 @@ func GetTFLatest(hashiURL string) (string, error) { } //GetTFLatestImplicit : Get the latest implicit terraform version given the hashicorp url -func GetTFLatestImplicit(hashiURL string, preRelease bool, version string) (string, error) { +func GetTFLatestImplicit(mirrorURL string, preRelease bool, version string) (string, error) { - result, error := GetTFURLBody(hashiURL) + result, error := GetTFURLBody(mirrorURL) if error != nil { return "", error } @@ -93,15 +93,19 @@ func GetTFLatestImplicit(hashiURL string, preRelease bool, version string) (stri } //GetTFURLBody : Get list of terraform versions from hashicorp releases -func GetTFURLBody(hashiURL string) ([]string, error) { +func GetTFURLBody(mirrorURL string) ([]string, error) { - resp, errURL := http.Get(hashiURL) + resp, errURL := http.Get(mirrorURL) if errURL != nil { log.Printf("Error getting url: %v", errURL) return nil, errURL } defer resp.Body.Close() + if resp.StatusCode != 200 { + log.Printf("Error retrieving contents from url: %s", mirrorURL) + } + body, errBody := ioutil.ReadAll(resp.Body) if errBody != nil { log.Printf("Error reading body: %v", errBody) diff --git a/main.go b/main.go index da0a25b6..8f7ce2c4 100644 --- a/main.go +++ b/main.go @@ -44,7 +44,7 @@ import ( ) const ( - hashiURL = "https://releases.hashicorp.com/terraform/" + defaultMirror = "https://releases.hashicorp.com/terraform/" defaultBin = "/usr/local/bin/terraform" //default bin installation dir defaultLatest = "" tfvFilename = ".terraform-version" @@ -61,6 +61,7 @@ func main() { latestPre := getopt.StringLong("latest-pre", 'p', defaultLatest, "Latest pre-release implicit version. Ex: tfswitch --latest-pre 0.13 downloads 0.13.0-rc1 (latest)") latestStable := getopt.StringLong("latest-stable", 's', defaultLatest, "Latest implicit version. Ex: tfswitch --latest-stable 0.13 downloads 0.13.7 (latest)") latestFlag := getopt.BoolLong("latest", 'u', "Get latest stable version") + custMirror := getopt.StringLong("mirror", 'm', defaultMirror, "Install from a remote other than the default. Default: https://releases.hashicorp.com/terraform") versionFlag := getopt.BoolLong("version", 'v', "Displays the version of tfswitch") helpFlag := getopt.BoolLong("help", 'h', "Displays help message") _ = versionFlag @@ -114,139 +115,139 @@ func main() { /* show all terraform version including betas and RCs*/ case *listAllFlag: listAll := true //set list all true - all versions including beta and rc will be displayed - installOption(listAll, &binPath) + installOption(listAll, &binPath, custMirror) /* latest pre-release implicit version. Ex: tfswitch --latest-pre 0.13 downloads 0.13.0-rc1 (latest) */ case *latestPre != "": preRelease := true - installLatestImplicitVersion(*latestPre, custBinPath, preRelease) + installLatestImplicitVersion(*latestPre, custBinPath, custMirror, preRelease) /* latest implicit version. Ex: tfswitch --latest 0.13 downloads 0.13.5 (latest) */ case *latestStable != "": preRelease := false - installLatestImplicitVersion(*latestStable, custBinPath, preRelease) + installLatestImplicitVersion(*latestStable, custBinPath, custMirror, preRelease) /* latest stable version */ case *latestFlag: - installLatestVersion(custBinPath) + installLatestVersion(custBinPath, custMirror) /* version provided on command line as arg */ case len(args) == 1: - installVersion(args[0], &binPath) + installVersion(args[0], &binPath, custMirror) /* provide an tfswitchrc file (IN ADDITION TO A TOML FILE) */ case fileExists(RCFile) && len(args) == 0: readingFileMsg(rcFilename) tfversion := retrieveFileContents(RCFile) - installVersion(tfversion, &binPath) + installVersion(tfversion, &binPath, custMirror) /* if .terraform-version file found (IN ADDITION TO A TOML FILE) */ case fileExists(TFVersionFile) && len(args) == 0: readingFileMsg(tfvFilename) tfversion := retrieveFileContents(TFVersionFile) - installVersion(tfversion, &binPath) + installVersion(tfversion, &binPath, custMirror) /* if versions.tf file found (IN ADDITION TO A TOML FILE) */ case checkTFModuleFileExist(dir) && len(args) == 0: - installTFProvidedModule(dir, &binPath) + installTFProvidedModule(dir, &binPath, custMirror) /* if Terraform Version environment variable is set */ case checkTFEnvExist() && len(args) == 0 && version == "": tfversion := os.Getenv("TF_VERSION") fmt.Printf("Terraform version environment variable: %s\n", tfversion) - installVersion(tfversion, custBinPath) + installVersion(tfversion, custBinPath, custMirror) /* if terragrunt.hcl file found (IN ADDITION TO A TOML FILE) */ case fileExists(TGHACLFile) && len(args) == 0: - installTGHclFile(&TGHACLFile, &binPath) + installTGHclFile(&TGHACLFile, &binPath, custMirror) // if no arg is provided - but toml file is provided case version != "": - installVersion(version, &binPath) + installVersion(version, &binPath, custMirror) default: listAll := false //set list all false - only official release will be displayed - installOption(listAll, &binPath) + installOption(listAll, &binPath, custMirror) } /* show all terraform version including betas and RCs*/ case *listAllFlag: - installWithListAll(custBinPath) + installWithListAll(custBinPath, custMirror) /* latest pre-release implicit version. Ex: tfswitch --latest-pre 0.13 downloads 0.13.0-rc1 (latest) */ case *latestPre != "": preRelease := true - installLatestImplicitVersion(*latestPre, custBinPath, preRelease) + installLatestImplicitVersion(*latestPre, custBinPath, custMirror, preRelease) /* latest implicit version. Ex: tfswitch --latest 0.13 downloads 0.13.5 (latest) */ case *latestStable != "": preRelease := false - installLatestImplicitVersion(*latestStable, custBinPath, preRelease) + installLatestImplicitVersion(*latestStable, custBinPath, custMirror, preRelease) /* latest stable version */ case *latestFlag: - installLatestVersion(custBinPath) + installLatestVersion(custBinPath, custMirror) /* version provided on command line as arg */ case len(args) == 1: - installVersion(args[0], custBinPath) + installVersion(args[0], custBinPath, custMirror) /* provide an tfswitchrc file */ case fileExists(RCFile) && len(args) == 0: readingFileMsg(rcFilename) tfversion := retrieveFileContents(RCFile) - installVersion(tfversion, custBinPath) + installVersion(tfversion, custBinPath, custMirror) /* if .terraform-version file found */ case fileExists(TFVersionFile) && len(args) == 0: readingFileMsg(tfvFilename) tfversion := retrieveFileContents(TFVersionFile) - installVersion(tfversion, custBinPath) + installVersion(tfversion, custBinPath, custMirror) /* if versions.tf file found */ case checkTFModuleFileExist(dir) && len(args) == 0: - installTFProvidedModule(dir, custBinPath) + installTFProvidedModule(dir, custBinPath, custMirror) /* if terragrunt.hcl file found */ case fileExists(TGHACLFile) && len(args) == 0: - installTGHclFile(&TGHACLFile, custBinPath) + installTGHclFile(&TGHACLFile, custBinPath, custMirror) /* if Terraform Version environment variable is set */ case checkTFEnvExist() && len(args) == 0: tfversion := os.Getenv("TF_VERSION") fmt.Printf("Terraform version environment variable: %s\n", tfversion) - installVersion(tfversion, custBinPath) + installVersion(tfversion, custBinPath, custMirror) // if no arg is provided default: listAll := false //set list all false - only official release will be displayed - installOption(listAll, custBinPath) + installOption(listAll, custBinPath, custMirror) } } /* Helper functions */ // install with all possible versions, including beta and rc -func installWithListAll(custBinPath *string) { +func installWithListAll(custBinPath, custMirror *string) { listAll := true //set list all true - all versions including beta and rc will be displayed - installOption(listAll, custBinPath) + installOption(listAll, custBinPath, custMirror) } // install latest stable tf version -func installLatestVersion(custBinPath *string) { - tfversion, _ := lib.GetTFLatest(hashiURL) - lib.Install(tfversion, *custBinPath) +func installLatestVersion(custBinPath, custMirror *string) { + tfversion, _ := lib.GetTFLatest(*custMirror) + lib.Install(tfversion, *custBinPath, *custMirror) } // install latest - argument (version) must be provided -func installLatestImplicitVersion(requestedVersion string, custBinPath *string, preRelease bool) { +func installLatestImplicitVersion(requestedVersion string, custBinPath, custMirror *string, preRelease bool) { if lib.ValidMinorVersionFormat(requestedVersion) { - tfversion, _ := lib.GetTFLatestImplicit(hashiURL, preRelease, requestedVersion) - lib.Install(tfversion, *custBinPath) + tfversion, _ := lib.GetTFLatestImplicit(*custMirror, preRelease, requestedVersion) + lib.Install(tfversion, *custBinPath, *custMirror) } else { printInvalidMinorTFVersion() } } // install with provided version as argument -func installVersion(arg string, custBinPath *string) { +func installVersion(arg string, custBinPath *string, custMirror *string) { if lib.ValidVersionFormat(arg) { requestedVersion := arg listAll := true //set list all true - all versions including beta and rc will be displayed - tflist, _ := lib.GetTFList(hashiURL, listAll) //get list of versions + tflist, _ := lib.GetTFList(*custMirror, listAll) //get list of versions exist := lib.VersionExist(requestedVersion, tflist) //check if version exist before downloading it if exist { - lib.Install(requestedVersion, *custBinPath) + lib.Install(requestedVersion, *custBinPath, *custMirror) } else { fmt.Println("The provided terraform version does not exist. Try `tfswitch -l` to see all available versions.") os.Exit(1) @@ -359,11 +360,11 @@ func usageMessage() { /* installOption : displays & installs tf version */ /* listAll = true - all versions including beta and rc will be displayed */ /* listAll = false - only official stable release are displayed */ -func installOption(listAll bool, custBinPath *string) { - tflist, _ := lib.GetTFList(hashiURL, listAll) //get list of versions - recentVersions, _ := lib.GetRecentVersions() //get recent versions from RECENT file - tflist = append(recentVersions, tflist...) //append recent versions to the top of the list - tflist = lib.RemoveDuplicateVersions(tflist) //remove duplicate version +func installOption(listAll bool, custBinPath, custMirror *string) { + tflist, _ := lib.GetTFList(*custMirror, listAll) //get list of versions + recentVersions, _ := lib.GetRecentVersions() //get recent versions from RECENT file + tflist = append(recentVersions, tflist...) //append recent versions to the top of the list + tflist = lib.RemoveDuplicateVersions(tflist) //remove duplicate version /* prompt user to select version of terraform */ prompt := promptui.Select{ @@ -379,23 +380,23 @@ func installOption(listAll bool, custBinPath *string) { os.Exit(1) } - lib.Install(tfversion, *custBinPath) + lib.Install(tfversion, *custBinPath, *custMirror) os.Exit(0) } // install when tf file is provided -func installTFProvidedModule(dir string, custBinPath *string) { +func installTFProvidedModule(dir string, custBinPath, custMirror *string) { fmt.Printf("Reading required version from terraform file\n") module, _ := tfconfig.LoadModule(dir) tfconstraint := module.RequiredCore[0] //we skip duplicated definitions and use only first one - installFromConstraint(&tfconstraint, custBinPath) + installFromConstraint(&tfconstraint, custBinPath, custMirror) } // install using a version constraint -func installFromConstraint(tfconstraint *string, custBinPath *string) { +func installFromConstraint(tfconstraint *string, custBinPath, custMirror *string) { tfversion := "" - listAll := true //set list all true - all versions including beta and rc will be displayed - tflist, _ := lib.GetTFList(hashiURL, listAll) //get list of versions + listAll := true //set list all true - all versions including beta and rc will be displayed + tflist, _ := lib.GetTFList(*custMirror, listAll) //get list of versions fmt.Printf("Reading required version from constraint: %s\n", *tfconstraint) constrains, err := semver.NewConstraint(*tfconstraint) //NewConstraint returns a Constraints instance that a Version instance can be checked against @@ -422,7 +423,7 @@ func installFromConstraint(tfconstraint *string, custBinPath *string) { tfversion = element.String() fmt.Printf("Matched version: %s\n", tfversion) if lib.ValidVersionFormat(tfversion) { //check if version format is correct - lib.Install(tfversion, *custBinPath) + lib.Install(tfversion, *custBinPath, *custMirror) } else { printInvalidTFVersion() os.Exit(1) @@ -435,7 +436,7 @@ func installFromConstraint(tfconstraint *string, custBinPath *string) { } // Install using version constraint from terragrunt file -func installTGHclFile(tgFile *string, custBinPath *string) { +func installTGHclFile(tgFile *string, custBinPath, custMirror *string) { fmt.Printf("Terragrunt file found: %s\n", *tgFile) parser := hclparse.NewParser() file, diags := parser.ParseHCLFile(*tgFile) //use hcl parser to parse HCL file @@ -445,7 +446,7 @@ func installTGHclFile(tgFile *string, custBinPath *string) { } var version terragruntVersionConstraints gohcl.DecodeBody(file.Body, nil, &version) - installFromConstraint(&version.TerraformVersionConstraint, custBinPath) + installFromConstraint(&version.TerraformVersionConstraint, custBinPath, custMirror) } type terragruntVersionConstraints struct {