Skip to content

Commit

Permalink
added option for custom remote mirror
Browse files Browse the repository at this point in the history
  • Loading branch information
warrensbox committed Jul 5, 2021
1 parent 71292d4 commit fcd8f7b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 83 deletions.
30 changes: 4 additions & 26 deletions lib/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand Down
20 changes: 12 additions & 8 deletions lib/list_versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -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)
Expand Down
99 changes: 50 additions & 49 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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{
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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 {
Expand Down

0 comments on commit fcd8f7b

Please sign in to comment.